最后
2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。
更多JVM面试整理:
如下就是一个泛型的定义示例:
package com.zwx.coreJava.genericity;
public class GenericMethod {
public T test(T arg){
System.out.println(“generic method”);
return null;
}
}
那么定义了泛型的方法应该如何调用呢?
我们把上面例子中的参数改为多参数看一下
package com.zwx.coreJava.genericity;
public class GenericMethod {
public T test(T…arg){
System.out.println(“generic method”);
return null;
}
public static void main(String[] args) {
GenericMethod genericMethod = new GenericMethod();
genericMethod.test(“1”,1);
}
}
上面这段代码可以正常编译输出,并没有起到限制的作用,所以如果非要限制,可以采用如下调用方式:
genericMethod.test(“1”,1);
在方法前面加上泛型就可以起到限制作用,这时候代码就会编译不通过了。
==================================================================
上面的泛型定义实际上并没有太多限制,还是可以传入任意类型,那么假如我想限制一下泛型,比如说只能是某一个类的子类,又该如何定义呢?
如果想限定泛型为某一个类的子类,那么就需要用到extends关键字。注意:泛型的继承只能使用extends,而不能使用implements(即使是接口也只能用extends关键字)。
package com.zwx.coreJava.genericity;
import java.io.Serializable;
public class GenericClass2{
public static void main(String[] args) {
GenericClass2 genericClass2 = new GenericClass2();
}
}
上面这个示例就会编译不通过,因为我们的MyItem类并没有实现Serializable接口,只有实现了Serializable接口的类才能被正常传进去。
===================================================================
在泛型里面支持多继承,可以通过“&”或者“,”来实现。
我们先看一个&的例子:
package com.zwx.coreJava.genericity;
import java.io.Serializable;
public class GenericClass2<T extends MyItem & Serializable & Cloneable>{
public static void main(String[] args) {
GenericClass2 genericClass2 = new GenericClass2();
}
}
这里要求必须只能有一个类,而且如果有一个类,那么这个类必须放在第一位。如上面的例子,MyItem只能放在第一位。而下面这句话需要编译通过,那么MyItemSub必须是如下定义:
public class MyItemSub extends MyItem implements Serializable,Cloneable {
}
也就是说必须同时满足所有条件。
接下来再看一个逗号的例子:
package com.zwx.coreJava.genericity;
public class GenericClass2<T extends MyItem,Serializable,Cloneable>{
public static void main(String[] args) {
GenericClass2 genericClass2 = new GenericClass2<MyItemSub,MyItemSub,MyItemSub>();
}
}
但是这种写法似乎只对第一个限制类型有用,后面的限制并没有用上,因为我除了第1个参数,后面2个参数我传任意类型都可以编译通过。
我们新建一个类继承MyItem:
package com.zwx.coreJava.genericity;
public class MyItemSub extends MyItem {
}
这时候我们再去测试一下这个例子:
package com.zwx.coreJava.genericity;
public class GenericClass3{
public static void main(String[] args) {
MyItemSub myItemSub = new MyItemSub();
MyItem myItem = myItemSub;//编译通过
GenericClass3 myItemSubGenericClass = new GenericClass3<>();
GenericClass3 myItemGenericClass = myItemSubGenericClass;//编译失败
}
}
可以看到最后这一句话编译不通过,也就是说:GenericClass3和GenericClass3没有任何关系(当然这个仅限于编译阶段),并不会因为泛型里面有继承关系就影响到了外面的继承关系。
==================================================================
在某些时候,我们可以通过通配符“?”来作为泛型,?表示不限制类型。(下面例子中Manager类是Employee的子类,两个类都是空类,就不贴代码了)
package com.zwx.coreJava.genericity.wildcard;
import java.util.ArrayList;
import java.util.List;
public class MyWildcard {
public static void main(String[] args) {
List<?> list = new ArrayList<>();
list.add(null);//编译通过
list.add(1);//报错
}
可以看到,虽然?表示允许任意类型,但是和List还是有本质区别的,使用通配符作为泛型的不能直接设置元素,除了null值。
不过虽然不能直接设值,但是我们可以通过间接的方式设置值进去。
package com.zwx.coreJava.genericity.wildcard;
import java.util.ArrayList;
import java.util.List;
public class MyWildcard {
public static void main(String[] args) {
List<?> list = MyWildcard.init();
Object obj = list.get(0);//通过
list.add(new Employee());//不通过
}
public static List<?> init(){
List list = new ArrayList<>();
list.add(new Employee());
return list;
}
}
虽然我不能直接设置值进去,但是我可以get获得list的元素。这是因为设置的时候无法确认?到底是什么类型,所以Java会拒绝编译,但是get元素的时候我可以直接赋值给Object,因为任何对象都是Object的子类,所以相当于知道了list内部对象的上界就是Object对象。用Object来接收不会有问题。
通配符也可以使用extends关键字。
package com.zwx.coreJava.genericity.wildcard;
import java.util.ArrayList;
import java.util.List;
public class MyWildcard {
public static void main(String[] args) {
List<? extends Employee> list = MyWildcard.init();
Employee employee = list.get(0);//通过
list.add(new Manager());//不通过
public static List<? extends Employee> init(){
List list = new ArrayList<>();
list.add(new Employee());
return list;
}
}
使用extends关键字和直接使用通配符区别不大,唯一的区别就是取值的时候我可以确定对象一定是Employee的子类,所以我可以直接使用Employee对象来接收。
换句话说就是:这种写法我可以知道list内部元素的上界,但是我不知道你的下界,所以我只允许取值(用上界接收),不允许设值(设值要用下界)
通配符还可以使用super关键字,限定类型为某个类的超类:
package com.zwx.coreJava.genericity.wildcard;
import java.util.ArrayList;
import java.util.List;
public class MyWildcard {
public static void main(String[] args) {
List<? super Employee> list = MyWildcard.init();
Employee employee = list.get(0);//不通过
Object obj = list.get(0);//编译通过
list.add(new Employee());//通过
}
public static List<? super Employee> init(){
List list = new ArrayList<>();
list.add(new Employee());
return list;
}
}
可以看到,这里就允许设值,因为super关键字限定了元素必须为Employee的父类,也就是我知道了元素的下限,所以我可以设值,但是取值因为不知道上界,所以只能用最顶层的Object。
======================================================================
这两种表示方法有区别吗?这两个有本质的区别。总结一下主要有如下区别:
-
1、T表示的是形参,也就是说如果我们想要用T,那么必须要先定义才能用;而?通配符则表示的是实参,也就是说不需要定义,我就可以直接拿来使用。
-
2、T相当于是占位符,如果我们定义了T,而实际传进去的是String,那么实际的约束类型就是String类型,而不是T;而?通配符可以理解为就是一种真实存在的特殊类型。
-
3、T可以使用多继承,而?通配符不行,只能用单继承
-
4、T不能使用super关键字,而?通配符可以使用super关键字。
-
5、?通配符表示的对象是存在继承关系的,而T表示的不同泛型之间没有任何关系,所以下面这个示例是成立的。
List<?> list = new ArrayList<>();
List<? extends Employee> list1 = new ArrayList<>();
list = list1;
=================================================================
我们先看一个例子:
package com.zwx.coreJava.genericity.erasure;
import com.zwx.coreJava.genericity.wildcard.Employee;
import java.util.ArrayList;
import java.util.List;
public class TestErasure {
public static void main(String[] args) {
最后
reJava.genericity.erasure;
import com.zwx.coreJava.genericity.wildcard.Employee;
import java.util.ArrayList;
import java.util.List;
public class TestErasure {
public static void main(String[] args) {
最后
[外链图片转存中…(img-AZHsjJpn-1715095549925)]
[外链图片转存中…(img-EmsGHBqD-1715095549926)]
[外链图片转存中…(img-TfDhmD6j-1715095549926)]