Java泛型 泛型类、方法、接口 、通配符、上下边界 泛型常见符号

一、泛型常见符号

E - Element (在集合中使用,因为集合中存放的是元素)

T - Type(Java 类)

K - Key(键)

V - Value(值)

N - Number(数值类型)

        ? -  表示不确定的java类型

S、U、V  - 2nd、3rd、4th types

源码中都比较常见

二、泛型类

主要是根据自己所需,在使用的时候再赋值类型

public class Generic<T> {
    private T t;
}

public class GenericTest {

    public static void main(String[] args) {
        Generic<String> generic = new Generic<>();
        generic.setT("string");
        generic.setT("");
       
    }
}

三、泛型方法

public class GenericMethod<T> {
    /**
     * 泛型普通方法
     *
     * @param t
     */
    public void say(T t) {
        System.out.println(t + "说话了");
    }

    /**
     * 静态泛型方法中的类型占位符和类中的占位符 毫无关系
     * 必须在static后面定义 占位符
     * 返回值为void
     */
    public static <T> void staticVoidSay(T t) {
        System.out.println(t + "这是一个静态泛型方法");
    }

    /**
     * 静态泛型方法必须自己声明泛型<p>
     * 返回值为 你传递进来的类型
     *
     * @param t   参数
     * @param <W> 类型
     * @return T
     */
    public static <W> W staticTSay(W t) {
        return t;
    }

    /**
     * @param r   传递的值
     * @param <R> 你想传递的类型
     */
    public <R> void differentSay(R r) {
        System.out.println(r + "说:这是一个与类泛型不同的普通泛型方法");
    }

}

四、泛型接口

//接口
public interface GenericInterface<T> {

    /**
     * 普通泛型接口 与泛型接口的类型一致
     *
     * @param t
     */
    void say(T t);
}

//普通实现
@Service
class PersonInterfaceStringImpl implements GenericInterface<String> {

    @Override
    public void say(String o) {
        System.out.println(o + "说话了");
    }
}

//泛型实现
@Service
class PersonInterfaceTImpl<T> implements GenericInterface<T> {

    @Override
    public void say(T t) {
        System.out.println(t + ", 在实现中定义了类型");
    }

    public static void main(String[] args) {
        //泛型接口的实现类可以指定具体的泛型接口的具体泛型类型
        GenericInterface<String> string = new PersonInterfaceStringImpl();
        string.say("xiao");

        //泛型接口的实现类 如果没有指定具体的泛型类型,必须要在这个实现类中声明一个类型占位符给接口使用
        GenericInterface t = new PersonInterfaceTImpl();
        t.say(2);
        t.say("小黄");
    }
}



五、泛型通配符上线边界


@Data
public class GenericWildcard<T> {

    private T name;

    public void say(GenericWildcard<T> GenericWildcard) {
        this.setName(GenericWildcard.getName());
        System.out.println("这是无法识别Java继承关系的的say:" + name);
    }
    //测试
    @Test
    void genericWildcardTest() {
        GenericWildcard<Number> numberGenericWildcard = new GenericWildcard<>();
        GenericWildcard<Integer> integerGenericWildcard = new GenericWildcard<>();
        //即便 Integer和Number 是父子关系,但是泛型当中它是无法识别的
        //numberGenericWildcard.say(integerGenericWildcard);
    }

    /**
     * ? 通配符 表示 任何类型 Object
     *
     * @param GenericWildcard 参数
     */
    public void sayWildcardObj(GenericWildcard<?> GenericWildcard) {
        //强制转换
        this.setName((T) GenericWildcard.getName());
        System.out.println("这是任意通配符?的say:" + name);
    }

    //测试
    @Test
    void GenericWildcardTest() {
        GenericWildcard<Number> numberGenericWildcard = new GenericWildcard<>();
        GenericWildcard<Integer> integerGenericWildcard = new GenericWildcard<>();
        //即便 Integer和Number 是父子关系,但是泛型当中它是无法识别的
        //numberGenericWildcard.say(integerGenericWildcard);

        integerGenericWildcard.setName(1111);
        //将它换为通配符?即可实现继承关系
        numberGenericWildcard.sayWildcardObj(integerGenericWildcard);
        //但是 不是继承关系也可以 因为 通配符?表示Object类型
        GenericWildcard<String> stringGenericWildcard = new GenericWildcard<>();
        stringGenericWildcard.setName("小明");
        numberGenericWildcard.sayWildcardObj(stringGenericWildcard);
    }

    /**
     * 上边界
     * <p>
     * 传递进来的值 必须为 特定类型的子类 或者 特定类型本身
     * <p>
     * ? extends T 传递进来的类型 必须 继承 T
     *
     * @param GenericWildcard 本身类型 or 子类类型
     */
    public void sayWildcardExtends(GenericWildcard<? extends T> GenericWildcard) {
        this.setName(GenericWildcard.getName());
        System.out.println("这是上边界 指定泛型类型 指定泛型可以传入 T 和 T的子类:" + name);
    }

    //测试
    @Test
    void test1(){
        GenericWildcard<Number> numberGenericWildcard = new GenericWildcard<>();
        GenericWildcard<Integer> integerGenericWildcard = new GenericWildcard<>();
        GenericWildcard<String> stringGenericWildcard = new GenericWildcard<>();
        //上边界 限制 Number 类型的子类 或者是Number类型本身
        numberGenericWildcard.sayWildcardExtends(integerGenericWildcard);
        //String不是Number类型的子类,所以不可传递
        //numberGenericWildcard.sayWildcardExtends(stringGenericWildcard);
    }


    /**
     * 下边界
     * 传递进来的值 必须为 特定类型的父类 或者 特定类型本身
     * ? super T 传递进来的类型 必须是T的父类
     *
     * @param GenericWildcard 必须是T或者T的父类
     */
    public void sayWildcardSuper(GenericWildcard<? super T> GenericWildcard) {
        this.setName((T) GenericWildcard.getName());
        System.out.println("这是下边界 泛型类型必须是T 或者T的父类:" + name);
    }

    //测试
    @Test
    void test(){
        GenericWildcard<Number> numberGenericWildcard = new GenericWildcard<>();
        GenericWildcard<Integer> integerGenericWildcard = new GenericWildcard<>();

        //下边界 限制Number类型 或者Number的父类
        numberGenericWildcard.sayWildcardSuper(numberGenericWildcard);
        //Integer不是Number的父类,所以不能船体
        //numberGenericWildcard.sayWildcardSuper(integerGenericWildcard);
    }

}

上界<? extends T>不能往里存,只能往外取

原因是编译器只知道容器内是Number或者它的派生类,但具体是什么类型不知道

下界<? super T>不影响往里存,但往外取只能放在Object对象里

因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Number的基类,那往里存粒度比Number小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。

PECS原则

最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:

  • 频繁往外读取内容的,适合用上界Extends。
  • 经常往里插入的,适合用下界Super。

版权声明:本文为Jesse_tan原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。