前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java泛型基础(二)泛型接口泛型方法# 总结

Java泛型基础(二)泛型接口泛型方法# 总结

作者头像
阿杜
发布于 2018-08-06 02:54:24
发布于 2018-08-06 02:54:24
3.4K00
代码可运行
举报
文章被收录于专栏:阿杜的世界阿杜的世界
运行总次数:0
代码可运行

本文首发于个人网站:Java中的泛型(二)

泛型可以应用于同一个类,该类可以针对多种类型使用,例如构建一个RedisTemplateService组件,用于处理当前应用中所有对象的缓存操作。这篇文章主要介绍泛型应用于接口、方法和匿名内部类的一些知识点和使用案例,也包括《Java编程思想》中对应的练习题的解读。

泛型接口

泛型应用于接口,是工厂方法设计模式的一种应用。我使用《Java编程思想》中的例子进行了练习。

下面这个例子中,CoffeeGenerator用于生成随机的Coffee对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.generics.coffee;

import org.apache.commons.lang3.RandomUtils;
import org.java.learn.util.Generator;

import java.util.Iterator;

/**
 * 实现Iterable接口,表示当前类可以用在循环语句中
 *
 * 作用: User: duqi Date: 2017/11/30 Time: 22:58
 */
public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
    private Class[] types = {Latte.class, Mocha.class, Cappuccino.class, Americano.class, Breve.class};
    private int size = 0;

    public CoffeeGenerator() {
    }

    /**
     * 末端哨兵,在case2中for-each语句中,告诉程序什么时候停止
     * @param size
     */
    public CoffeeGenerator(int size) {
        this.size = size;
    }

    @Override
    public Coffee next() {
        try {
            return (Coffee) types[RandomUtils.nextInt(0, types.length-1)].newInstance();
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }

    class CoffeeIterator implements Iterator<Coffee> {
        /**
         * 内部类可以直接访问外部类的属性
         */
        int count = size;

        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Coffee next() {
            count--;
            return CoffeeGenerator.this.next();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeIterator();
    }

    public static void main(String[] args) {
        //case1:测试CoffeeGenerator的next()方法;
        CoffeeGenerator gen = new CoffeeGenerator();
        for (int i = 0; i < 5; i++) {
            System.out.println(gen.next());
        }

        //case2: 测试在for-each语句中生成对象
        for (Coffee coffee: new CoffeeGenerator(5)) {
            System.out.println(coffee);
        }
    }
}

再看一个例子,使用Generator<T>接口生成Fibonacci数列。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.generics.coffee;

import org.java.learn.util.Generator;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 13:59
 */
public class Fibonacci implements Generator<Integer> {

    private int count = 0; //全部是int基本类型,但是类型参数是Integer

    @Override
    public Integer next() {
        return fib(count++);
    }

    private int fib(int n) {
        if (n < 2) {
            return 1;
        }
        return fib(n - 2) + fib(n - 1);
    }

    public static void main(String[] args) {
        Fibonacci fibonacci = new Fibonacci();
        for (int i = 0; i < 18; i++) {
            System.out.print(fibonacci.next() + " ");
        }
    }
}

如果希望将这个Fibonacci生成器用于循环语句,书中的例子用的是继承Fibonacci,写一个IterableFibonacci类,该类实现了Iterable接口。在练习7中,作者提示可以使用“组合代替继承”实现同样的功能,我尝试自己做了下,这是我的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.generics;

import java.util.Iterator;

/**
 * 组合代替继承,实现适配器模式
 *
 * IterableFibonacci2适配Fibonacci为可被循环语句使用的生成器
 *
 * 作用: User: duqi Date: 2017/12/2 Time: 14:08
 */
public class IterableFibonacci2 implements Iterable<Integer> {

    //末端哨兵
    private int n;

    private Fibonacci fibonacci; //组合代替继承

    public IterableFibonacci2(int n, Fibonacci fibonacci) {
        this.n = n;
        this.fibonacci = fibonacci;
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return n > 0;
            }

            @Override
            public Integer next() {
                n--;
                return fibonacci.next();
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    public static void main(String[] args) {
        for (int i: new IterableFibonacci2(18, new Fibonacci())) {
            System.out.print(i + " ");
        }
    }
}
  • 上面三个例子中,提到了两种设计模式:工厂方法设计模式适配器模式
  • 在泛型中,基本类型无法作为类型参数,但是Java提供了自动打包和拆包的功能;

泛型方法

知识点总结

  • 如果使用泛型方法可以取代将整个类(或接口)泛型化,那么就应该只使用泛型方法;
  • static方法要使用泛型能力,就必须成为泛型方法;
  • 类型推断:这是编译器的特性。在使用泛型类的时候,必须在创建对象的时候指定类型参数的值,但是在使用泛型方法时候,不必指明参数类型。
    • 类型推断只对赋值操作有效
  • 泛型方法与可变参数可以一起使用

例子1:使用Generator的泛型方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.generics;

import org.java.learn.generics.coffee.Coffee;
import org.java.learn.generics.coffee.CoffeeGenerator;
import org.java.learn.util.Generator;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 14:58
 */
public class Generators {

    /**
     * 泛型方法的定义格式——将泛型参数列表放在方法的返回值左面
     */
    public static <T> Collection<T> fill(Collection<T> coll, Generator<T> gen, int n) {
        for (int i = 0; i < n; i++) {
            coll.add(gen.next());
        }
        return coll;
    }

    public static void main(String[] args) {
        Collection<Coffee> coffees = fill(new ArrayList<>(), new CoffeeGenerator(), 4);
        for (Coffee coffee : coffees) {
            System.out.println(coffee);
        }

        Collection<Integer> numers = fill(new ArrayList<>(), new Fibonacci(), 12);
        for (int i : numers) {
            System.out.print(i + " ");
        }
    }
}

例子2:一个通用的Generator

下面这个例子,是一个通用的生成器,只需要传入指定的类型,就可以生成对应类型的对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.util;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 15:04
 */
public class BasicGenerator<T> implements Generator<T> {

    private Class<T> type;

    public BasicGenerator(Class<T> type) {
        this.type = type;
    }

    @Override
    public T next() {
        try {
            return type.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<>(type);
    }
}

上面这段代码可以创建什么类的对象呢?(1)public的类;(2)含有默认构造器的类;这里给出一个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.generics;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 15:10
 */
public class CountedObject {

    private static long counter = 0;
    private final long id = counter++;

    public long getId() {
        return id;
    }

    @Override
    public String toString() {
        return "CounteredObject " + id;
    }
}

然后再给出一个使用上述构造器的例子,书中的例子是使用BasicGenerator的create()方法,我这里还实现了练习14中提到的方法,参见:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.generics;

import org.java.learn.util.BasicGenerator;
import org.java.learn.util.Generator;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 15:11
 */
public class BasicGeneratorDemo {

    public static void main(String[] args) {
        Generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(gen.next());
        }

        //练习14:不使用create方法,使用显式的构造器
        Generator<CountedObject> generator = new BasicGenerator<>(CountedObject.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(generator.next());
        }
    }
}

例子3:简化元组的使用

之前的一篇文章里,已经实现过TwoTuple、ThreeTuple等工具类,但是使用的时候还不太方便,这里利用泛型方法实现一个工具类,可以简化元组的使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.util.tuple;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 15:21
 */
public class Tuple {

    public static <A, B> TwoTuple<A, B> tuple(A a, B b) {
        return new TwoTuple<>(a, b);
    }

    public static <A, B, C> ThreeTuple<A, B, C> tuple(A a, B b, C c) {
        return new ThreeTuple<>(a, b, c);
    }

    public static <A, B, C, D> FourTuple<A, B, C, D> tuple(A a, B b, C c, D d) {
        return new FourTuple<>(a, b, c, d);
    }
}

这个工具类的使用例子如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.util.tuple;

import org.java.learn.generics.coffee.Breve;
import org.java.learn.generics.coffee.Cappuccino;

import static org.java.learn.util.tuple.Tuple.*;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 15:24
 */
public class TupleTest {

    static TwoTuple<String, Integer> f() {
        return tuple("hi", 47);
    }

    static TwoTuple f2() {
        return tuple("hi", 47);
    }

    static ThreeTuple<Breve, String, Integer> g() {
        return tuple(new Breve(), "hi", 44);
    }

    static FourTuple<Cappuccino, Breve, String, Integer> h() {
        return tuple(new Cappuccino(), new Breve(), "hi", 447);
    }

    public static void main(String[] args) {
        TwoTuple<String, Integer> ttsi = f();
        System.out.println(ttsi);
        /**
         * 这里没有发出告警,是因为我们将f2()的返回值直接返回,并没有再尝试转为参数化对象;
         */
        System.out.println(f2());
        System.out.println(g());
        System.out.println(h());

        /**
         * 练习15:这里尝试将f2的返回值转为一个参数化对象,就收到了报警
         */
        TwoTuple<String, Integer> ttsi2 = f2();
    }
}

例子4:一个Set实用工具

书中提供了一个Sets工具类,用于实现常用的集合操作,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.java.learn.util;

import java.util.HashSet;
import java.util.Set;

/**
 * 作用: User: duqi Date: 2017/12/2 Time: 15:40
 */
public class Sets {

    /**
     * A和B的并集
     * @param a
     * @param b
     * @param <T>
     * @return
     */
    public static <T> Set<T> union(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<>(a);
        result.addAll(b);
        return result;
    }

    /**
     * A和B的交集
     * @param a
     * @param b
     * @param <T>
     * @return
     */
    public static <T> Set<T> intersection(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<>(a);
        result.retainAll(b);
        return result;
    }

    /**
     * A和B的差集,将A中移除B中的元素
     * @param superset
     * @param subset
     * @param <T>
     * @return
     */
    public static <T> Set<T> difference(Set<T> superset, Set<T> subset) {
        Set<T> result = new HashSet<>(superset);
        result.removeAll(subset);
        return result;
    }

    /**
     * A和B中所有的元素,减去A和B的交集,剩下的元素
     * @param a
     * @param b
     * @param <T>
     * @return
     */
    public static <T> Set<T> complement(Set<T> a, Set<T> b) {
        return difference(union(a, b), intersection(a, b));
    }

}

# 总结

  1. 本节涉及的知识点:泛型接口、泛型方法
  2. 本节练习用的代码:LearnJava
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.12.09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
Java泛型基础(一)目的泛型类总结
利用Java开发的时候,肯定会有一个类持有另一个或几个类的情况,在编写一些比较基础的组件,例如缓存操作组件,这类组件的逻辑差不多,但是希望能够处理不同的类型。
阿杜
2018/08/06
7180
大师的小玩具——泛型精解
掌握Java的泛型,这一篇文章足够了。 关键字:泛型,Iterable接口,斐波那契数列,匿名内部类,枚举,反射,可变参数列表,Set 一般类和方法,要么只能使用基础类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制会对代码的束缚很大。 Java中,当你将一个基类作为一个方法的参数传入的时候,所有该基类的子类均可以作为参数,然而private构造器或者final修饰的类是不可被继承的,再加上Java的单继承特性,这种看上去的灵活性也有他的限制以及性能损耗。 如果参数
文彬
2018/05/08
1.5K0
Java一分钟之——泛型方法与泛型接口
在Java编程的世界里,泛型(Generics)是一个强大的特性,它允许你在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用性和灵活性。本文将深入浅出地探讨泛型方法与泛型接口的核心概念、常见问题、易错点及避免策略,并通过具体代码示例加以说明
Jimaks
2024/05/28
4030
Java一分钟之——泛型方法与泛型接口
初识Java泛型
https://blog.csdn.net/weixin_44510615/article/details/102718400
润森
2019/10/30
4050
Java泛型总结
集合容器类“设计阶段/声明阶段”不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为 Object,JDK1.5 之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection<E>,List<E>,ArrayList<E>这个 <E> 就是类型参数,即泛型。
乐心湖
2021/01/18
8750
Java泛型总结
java泛型详解
泛型,即"参数化类型"。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
码农王同学
2020/04/08
1.4K0
Java 泛型示例 - 泛型方法,类,接口
如果您一直在使用Java Collections并使用版本 5 或更高版本,那么我确定您已经使用过它。
淡定的蜗牛
2019/10/25
2.8K0
Java泛型的局限和使用经验泛型的局限泛型的常用经验参考资料
//使用泛型类 @Data @Builder @AllArgsConstructor @NoArgsConstructor public class DataListPageInfo<T> { int page; int pageSize; int totalCount; List<T> items = new ArrayList<>(); }
阿杜
2018/08/06
9020
Java泛型的局限和使用经验泛型的局限泛型的常用经验参考资料
那些年我们在Java泛型上躺过的枪---万恶的泛型擦除【享学Java】
泛型(Generics),从字面的意思理解就是泛化的类型,即参数化类型。 我们都知道,泛型是JDK5提供的一个非常重要的新特性,它有非常多优秀的品质:能够把很多问题从运行期提前到编译器,从而使得程序更加的健壮。
YourBatman
2019/09/03
1K0
那些年我们在Java泛型上躺过的枪---万恶的泛型擦除【享学Java】
Java泛型详解
Dog对象含有name 和 age, 并输出name 和 age (要求使用getXxx())
timerring
2023/05/07
5040
Java泛型详解
Java基础系列2:Java泛型
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架。
说故事的五公子
2019/11/10
5750
JAVA中的泛型
程序在运行时发生了问题java.lang.ClassCastException。 为什么会发生类型转换异常呢? 我们来分析下:由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 ClassCastException。 怎么来解决这个问题呢?
星哥玩云
2022/09/14
1.6K0
JAVA中的泛型
Java-Java5.0泛型解读
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
小小工匠
2021/08/17
5450
Java泛型总结
泛型是jdk5引入的类型机制,就是将类型参数化,它是早在1999年就制定的jsr14的实现。
pollyduan
2019/11/04
1K0
第08天Java泛型机制
如果没有泛型,要实现不同类型的加法,每种类型都需要重载一个 add 方法;通过泛型,我们可以复用为一个方法:
程序员Leo
2023/09/09
2260
第08天Java泛型机制
Java从入门到精通九(Java泛型)
泛型是什么?我们在哪里会遇到? 比如在一些集合类里面,我们可以看到对于键值的参数化限制。作用就是指定了键值的类型。
兰舟千帆
2022/07/16
7060
Java从入门到精通九(Java泛型)
Java泛型是什么?
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
黑洞代码
2021/01/14
3460
Java知识点总结之Java泛型
作者:苏生 链接: https://segmentfault.com/a/1190000014824002 泛型 泛型就是参数化类型 适用于多种数据类型执行相同的代码 泛型中的类型在使用时指定 泛型归根到底就是“模版” 优点:使用泛型时,在实际使用之前类型就已经确定了,不需要强制类型转换。 泛型主要使用在集合中 import java.util.ArrayList; import java.util.List; public class Demo01 { // 不使用泛型,存取数据麻烦
用户1257393
2018/07/30
5750
关于对Java泛型的解释和思考
Generics are a facility of generic programming that were added to the Java programming language in 2004 within version J2SE 5.0. They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety".
互联网金融打杂
2022/08/01
6550
Java泛型详解
根据给出的文章内容,撰写摘要总结。
Java后端工程师
2017/12/16
1.8K0
相关推荐
Java泛型基础(一)目的泛型类总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验