Java中实例化泛型类时,可以省略泛型参数:
MimicStack<Integer> stack = new MimicStack<>(); //空<>,OK的
背景:Java在很长一段时间,拒不承认泛型的作用(觉得泛型破坏了面向对象的纯粹性),等到决定引入泛型的时候,Java已经是5.0版本了,为了兼容旧版本,Java不得不采用了一种被称之为泛型擦除(Type erasure)的技术。
#常见面试题#:什么是发现擦除?
即:泛型参数的类型检查仅发生在编译时。
一旦编译完成,泛型类就消失了,JVM会根据泛型参数约束(边界)的最高级类:
生成相应的类,比如:
class MimicStack<T> {
编译后就是:
class MimicStack {所以,
The type MimicStack is already defined
MimicStack stack = new MimicStack<Integer>(); // OK Object result = stack.pop(); //用Object接pop()返回值
泛型类中的:
public T pop() {会被编译成:
public Object pop() {如果是:
class MimicStack<T extends Student> {就会被编译成:
public Student pop() {
所以,这样的代码,一样不会报错:
MimicStack stack = new MimicStack<Student>(); Student result = stack.pop();
这给我讲课带来了不少的麻烦,^_^
T[] container = new T[];
报错:
Cannot create a generic array of T
咋整?最简单的一个办法
T[] container = (T[])new Object[10];
但是,会报警告(后文@SuppressWarnings可以消除)。体会:程序员无视警告
@想一想@:为什么能行?
其他参考:How to create a generic array in Java?
直接:
return new T();
会报错的:
Cannot instantiate the type T
怎么办呢?从Java8开始,可以引入supplier(后文详述):
import java.util.function.Supplier;
然后,将supplier对象作为构造函数参数传入:
private Supplier<T> supplier; public MimicStack(Supplier<T> supplier) { this.supplier = supplier; }实例化这个泛型类的时候,用这种语法:
MimicStack<Student> stack = new MimicStack<>(Student::new);最后,可以调用supplier的get()方法,拿到一个泛型参数类型实例:
return supplier.get();
其他参考:How do I get a class instance of generic type T?
static T obj; //静态成员类型 static T pop() { //静态方法返回值 static void push(T obj) { //静态方法参数
这个……,克服一下吧,只有。本来我们也是鼓励实例,不鼓励静态的。
但是,静态的泛型方法是OK的:
public static <T> T pop() {
调用这种无法从参数类型推导出T的类型的方法时,可以在方法名前加尖括号:
MimicStack.<Integer>pop();
MimicStack<int> stack = new MimicStack<int>();
为什么就不行呢?很多文章解释是因为泛型擦除。
但其实这种解释是比较牵强的,因为基本(值)类型仍然可以装箱拆箱完成转换,于是大家觉得是因为性能的原因……(参考:Why don't Java Generics support primitive types?)
个人觉得,根本的原因,还是Java追求纯粹的面向对象的原因:int不是对象,Integer才是。结果就是:
fully agree on that it's bad design which endlessly hurts beginners ano professionals alike
PS:公认的,语法特性上,Java的没有C#友好——实际上,差远了。
MimicStack<Person> mp = new MimicStack<Student>();
使用通配符?表示任何类,
MimicStack<?> mp = new MimicStack<Person>();
还可以培训关键字:
MimicStack<? extends Person> mp = new MimicStack<Student>();
MimicStack<? super Student> mp = new MimicStack<Person>();
@想一想@:这有啥用呢?
MimicStack<? extends Person> mp = null; Person p = mp.pop();
MimicStack<? super Person> mp = null; mp.push(new Student());
PS:对应C#中in/out(协变逆变)
#常见面试题#:开发中你最常见的错误/异常是什么?
当然是NullPointer异常:你使用的对象并不总是自己new出来的,而更可能是其他地方传过来的,传过来的对象很有可能就是null值。为了不报错,你的代码就会有大量的类似代码:Student student = null; // 从其他地方获得的student对象 if (student == null) { } else { }
看着很烦。(你觉得烦不烦?不觉得就说明功力不够)
于是Java8内置了Optional泛型类,你可以把它想象成一个容器,里面存放可能为null值的对象。对象类型不定,所以要使用泛型。
获取Optional对象可以通过调用其静态方法ofNullable():
Optional<Student> os = Optional.ofNullable(student);不要使用of()方法,因为of()方法的参数不能为null,没有什么意义。
它提供了orElse()方法,当存放值为null时,可以用其他值替代,避免NullPointer异常:
//这样得到的Student对象永远不会为null Student neverNull = os.orElse(new Student());
不建议这样使用Optional:
if (os.isPresent()) { //检查os中值是否为null os.get(); //从os中取出不为null的对象 }
因为这样就和null值判断没有什么区别了。
PS:可对照C#的可空类型
多快好省!前端后端,线上线下,名师精讲
更多了解 加: