Java泛型知识整理

假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用  Java 泛型
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型将原来的具体的类型参数化,
类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),
然后在使用/调用时传入具体的类型(类型实参)。泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型
我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?
常用的 T,E,K,V,?
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,
并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:
  • E - Element (在集合中使用,因为集合中存放的是元素),E是对各方法中的泛型类型进行限制,以保证同一个对象调用不同的方法时,操作的类型必定是相同的。E可以用其它任意字母代替
  • T - Type(Java 类),T代表在调用时的指定类型。会进行类型推断
  • K - Key(键)
  • V - Value(值)
  • N - Number(数值类型)
  • ? - 表示不确定的java类型,是类型通配符,代表所有类型。?不会进行类型推断
public void cycleList(List<String> list) 可以用  public void cycleList(List<Integer> list)  重载还是重写?
重载规则---必须具有不同的参数列表,可以有不同的返回类型;可以有不同的访问修饰符;可以抛出不同的异常。
重写规则---参数列表必须完全与被重写的方法相同,否则不能称其为重写;返回类型必须与被重写的方法相同,否则不能称其为重写
尽管两个cycleList方法具有相同的字节码,但是类型参数信息用 一个新的签名(signature) 属性记录在类模式中。JVM 在装载类时记录这个签名信息,
并在运行时通过反射使它可用。这就导致了这个方法既不能作为覆盖父类cycleList方法的方法,也不能作为cycleList方法的重载。
 
public class GenericTest {
    public static void main(String[] args) {
        Box<String> name = new Box<String>("corn");
        Box<Integer> age = new Box<Integer>(712);
        System.out.println("name class:" + name.getClass());      // com.xx.Box
        System.out.println("age class:" + age.getClass());        // com.xx.Box
        System.out.println(name.getClass() == age.getClass());    // true
    }
}
 
由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,
即还是原来的最基本的类型(本实例中为Box),当然,在逻辑上我们可以理解成多个不同的泛型类型。
究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,
也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
1 泛型接口 泛型类
类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,
是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
2 )泛型方法
根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。
每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
3 )类型通配符
类型通配符一般是使用?代替具体的类型参数。例如  List<?>  在逻辑上是 List<String>,List<Integer>  等所有List<具体类型实参>的父类。
有界的类型参数  <? extends T> 和  <? super T>
   可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
 
public class MaximumTest {
    public static <T extends Comparable<T>> T maxnum(T t1, T t2, T t3) {
        T max = t1;
        if (t2.compareTo(max) > 0) {
            max = t2;
        }
        if (t3.compareTo(max) > 0) {
            max = t3;
        }
        return max;
    }
    public static void main(String[] args) {
        System.out.println("maxnum(1,2,3) = " + maxnum(1, 2, 3));
        System.out.println("maxnum(1.1,3.3,2.2) = " + maxnum(1.1, 3.3, 2.2));
        System.out.println("maxnum(\"abc\",\"cda\",\"bdf\") = " + maxnum("abc", "cda", "bdf"));
    }
}
 
 
 
上界通配符Plate<? extends Fruit>覆盖下图中蓝色的区域
下界通配符 Plate<? super Fruit>覆盖下图中红色的区域
PECS原则总结
从上述两方面的分析,总结PECS(Producer Extends Consumer Super)原则如下:
  • 如果要从集合中读取类型T的数据,并且不能写入(违反类型安全),可以使用 ? extends 通配符;(Producer Extends)
  • 如果要从集合中写入类型T的数据,并且不需要读取(父类转换成子类必会失败 ),可以使用 ? super 通配符;(Consumer Super)
  • 如果既要存又要取,那么就不要使用任何通配符。
 
 
 
 
 
 
 
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值