原创 2017-11-21 工匠若水 码农每日一题
关注一下嘛,又不让你背锅!
问:Java 的泛型是什么?有什么好处和优点?JDK 不同版本的泛型有什么区别?
答:泛型是 Java SE 1.5 的新特性,泛型的本质是参数化类型,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。在 Java SE 1.5 之前没有泛型的情况的下只能通过对类型 Object 的引用来实现参数的任意化,其带来的缺点是要做显式强制类型转换,而这种强制转换编译期是不做检查的,容易把问题留到运行时,所以 泛型的好处是在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率,避免在运行时出现 ClassCastException。
JDK 1.5 引入了泛型来允许强类型在编译时进行类型检查;JDK 1.7 泛型实例化类型具备了自动推断能力,譬如 List<String> list = new ArrayList<String>(); 可以写成 List<String> llist = new ArrayList<>(); 了,JDK 具备自动推断能力。下面几种写法可以说是不同版本的兼容性了:
//JDK 1.5 推荐使用的写法
List<String> list =new ArrayList<String>();
//JDK 1.7 推荐使用的写法
List<String> llist =new ArrayList<>();
//可以使用,但不推荐,是为了兼容老版本,IDE 会提示警告,可以通过注解屏蔽警告
List<String> list =new ArrayList();
//可以使用,但不推荐,是为了兼容老版本,IDE 会提示警告,可以通过注解屏蔽警告
List list =new ArrayList<String>();
问:Java 泛型是如何工作的?什么是类型擦除?
答:泛型是通过类型擦除来实现的,编译器在编译时擦除了所有泛型类型相关的信息,所以在运行时不存在任何泛型类型相关的信息,譬如 List<Integer> 在运行时仅用一个 List 来表示,这样做的目的是为了和 Java 1.5 之前版本进行兼容。泛型擦除具体来说就是在编译成字节码时首先进行类型检查,接着进行类型擦除(即所有类型参数都用他们的限定类型替换,包括类、变量和方法),接着如果类型擦除和多态性发生冲突时就在子类中生成桥方法解决,接着如果调用泛型方法的返回类型被擦除则在调用该方法时插入强制类型转换。
问:Java 泛型类、泛型接口、泛型方法有什么区别?
答:泛型类是在实例化类的对象时才能确定的类型,其定义譬如 class Test<T> {},在实例化该类时必须指明泛型 T 的具体类型。
泛型接口与泛型类一样,其定义譬如 interface Generator<E> { E dunc(E e); }。
泛型方法所在的类可以是泛型类也可以是非泛型类,是否拥有泛型方法与所在的类无关,所以在我们应用中应该尽可能使用泛型方法,不要放大作用空间,尤其是在 static 方法时 static 方法无法访问泛型类的类型参数,所以更应该使用泛型的 static 方法(声明泛型一定要写在 static 后返回值类型前)。泛型方法的定义譬如 <T> void func(T val) {}。
问:Java 如何优雅的实现元组?
答:元组其实是关系数据库中的一个学术名词,一条记录就是一个元组,一个表就是一个关系,纪录组成表,元组生成关系,这就是关系数据库的核心理念。很多语言天生支持元组,譬如 Python 等,在语法本身支持元组的语言中元组是用括号表示的,如 (int, bool, string) 就是一个三元组类型,不过在 Java、C 等语言中就比较坑爹,语言语法本身不具备这个特性,所以在 Java 中我们如果想优雅实现元组就可以借助泛型类实现,如下是一个三元组类型的实现:
Triplet<A,B,C>{
private A a;
private B a;
private C a;
public Triplet(A a,B b,C c){
this.a =a;
this.b =b;
this.c =c;
}
}
泛型多灾多难,也是面试重灾区,所以这个系列会循序渐进多篇~