
关于泛型,关于有一道经典的泛型考题:
public static void main(String[] args) { List<String> list1= new ArrayList<String>(); List<Integer> list2= new ArrayList<Integer>(); System.out.println(list1.getClass() == list2.getClass()); } 请问上面代码的输出结果是什么?
如果是了解泛型的同学会很容易答出:true,如果是关于不了解泛型的同学则很可能会答错。今天就和大家一起来重温一下Java泛型相关的泛型知识。
一、关于什么是泛型泛型?
泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的关于类型。泛型的泛型本质是参数化类型,也就是关于说所操作的数据类型被指定为一个参数。具有以下特点:
与普通的泛型 Object 代替一切类型这样简单粗暴而言,泛型使得数据的关于类别可以像参数一样由外部传递进来。它提供了一种扩展能力。泛型它更符合面向抽象开发的关于软件编程宗旨。 当具体的泛型类型确定后,源码下载泛型又提供了一种类型检测的关于机制,只有相匹配的数据才能正常的赋值,否则编译器就不通过。所以说,它是一种类型安全检测机制,一定程度上提高了软件的安全性防止出现低级的失误。 泛型提高了程序代码的可读性,不必要等到运行的时候才去强制转换,在定义或者实例化阶段,因为 Cache这个类型显化的效果,程序员能够一目了然猜测出代码要操作的数据类型。 泛型按照使用情况可以分为3种:泛型类、泛型方法、泛型接口。
1.泛型类
我们可以定义如下一个泛型类
/** * @author machongjia * @date 2021/12/28 20:02 * @description */ public class Generic<T> { private T var; public Generic(T var) { this.var = var; } public T getVar() { return var; } public static void main(String[] args) { Generic<Integer> i = new Generic<Integer>(1000); Generic<String> s = new Generic<String>("hello"); System.out.println(i.getVar()); System.out.println(s.getVar()); } } 输出结果:
1000 hello 常用的类似于T这样的类型参数包括:
T:代表一般的任何类
E:代表 Element 的意思,或者 Exception 异常的意思
K:代表 Key 的意思。
V:代表 Value 的源码库意思,通常与 K 一起配合使用
S:代表 Subtype 的意思
泛型类可以不止接受一个参数T,还可以接受多个参数,类似于下面这种:
public class Generic<E,T> { private E var1; private T var2; public Generic(E var1, T var2) { this.var1 = var1; this.var2 = var2; } public static void main(String[] args) { Generic<Integer,String> generic = new Generic<Integer,String>(1000,"hello"); System.out.println(generic.var1); System.out.println(generic.var2); } } 2.泛型方法
public class Generic { public <T> void testMethod(T t){ } } 泛型方法与泛型类稍有不同的地方是,类型参数也就是尖括号那一部分是写在返回值前面的。中的 T 被称为类型参数,而方法中的 T 被称为参数化类型,它不是运行时真正的参数。
当然,声明的类型参数,其实也是可以当作返回值的类型的。
泛型类与泛型方法共存的情况:
public class Generic<T> { public void testMethod(T t){ System.out.println(t.getClass().getName()); } public <T> T testMethod1(T t){ return t; } } 上面代码中,Test1是泛型类,testMethod 是泛型类中的普通方法,而 testMethod1 是亿华云一个泛型方法。而泛型类中的类型参数与泛型方法中的类型参数是没有相应的联系的,泛型方法始终以自己定义的类型参数为准。
3.泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
//定义一个泛型接口 public interface Generator<T> { public T next(); } 当实现泛型接口的类,未传入泛型实参时:
/** * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 * 即:class FruitGenerator<T> implements Generator<T>{ * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class" */ class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } } 当实现泛型接口的类,传入泛型实参时:
/** * 传入泛型实参时,定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T> * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型 * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。 */ public class FruitGenerator implements Generator<String> { private String[] fruits = new String[]{ "Apple", "Banana", "Pear"}; @Override public String next() { Random rand = new Random(); return fruits[rand.nextInt(3)]; } } 4.通配符?
通配符的出现是为了指定泛型中的类型范围,包含以下3 种形式。
<?>被称作无限定的通配符。 <? extends T>被称作有上限的通配符。 <? super T>被称作有下限的通配符。 无限定通配符<?>
无限定通配符经常与容器类配合使用,它其中的 ? 其实代表的是未知类型,所以涉及到 ? 时的操作,一定与具体类型无关。
public void testWildCards(Collection<?> collection){ } 上面的代码中,方法内的参数是被无限定通配符修饰的 Collection 对象,它隐略地表达了一个意图或者可以说是限定,那就是 testWidlCards() 这个方法内部无需关注 Collection 中的真实类型,因为它是未知的。所以,你只能调用 Collection 中与类型无关的方法。
