本文为原创文章,转载请注明出处
查看[Java]系列内容请点击:https://www.jianshu.com/nb/45938443
限定必需继承自某个类
当我们需要限定泛型对象必需是继承自某一个类或者接口的时候,我们使用:
<T extends MyInterface>
例如以下代码:
public interface MyInterface {
void run();
}
public class MainTest {
public static void main(String[] args) {
}
// 不限定类型,需要强制转换
public static <T> void fun1(T obj) {
MyInterface myInterface = (MyInterface) obj;
myInterface.run();
}
// 限定类型,不需要强制转换
public static <T extends MyInterface> void fun2(T obj) {
obj.run();
}
}
对比fun1
和fun2
方法,可以看到,fun2
限定了T
必需是MyInterface
的子类型,从而可以直接调用MyInterface
接口中的方法,而不需要强制转换。
可以定义多个复杂的限定类型:
public static <T extends MyInterface & MyInterface1, U extends MyInterface> void fun2(T obj, U obj1)
那么所传的obj
参数必需同时继承自MyInterface
和MyInterface1
两个父接口,obj1
必需继承自MyInterface
接口。
我们称MyInterface
和MyInterface1
是T
的超类型,T
可以有多个超类型,超类型最多只允许有一个是类,且类必需是限定列表中的第一个,其余都是接口(与Java的继承机制保持一致)。
问号通配符及其由来
通配符由来
考虑下面一段程序:
public class MainTest {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>() {{
put("1", "1");
}};
/*
* 这里出错,因为 Map<String, String> 不是 Map<String, Object> 类型的子类型,
* 所以参数类型不匹配
* */
fun1(map);
}
public static void fun1(Map<String, Object> map) {
System.out.println(map);
}
}
在调用fun1
方法的时候,实际需要的参数类型是Map<String, Object>
类型,而实际传参是Map<String, String>
类型,所以这里就会报错:参数类型不匹配。
那么,怎么解决这个问题?可以考虑由通配符来解决问题。
问号通配符
下面列出使用继承等方式分三种方式解决问题:
public class MainTest {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>() {{
put("1", "1");
}};
// 正确
fun1(map);
fun2(map);
fun3(map);
}
public static <T extends Object> void fun1(Map<String, T> map) {
T a = null; // 可以使用变量类型T
System.out.println(map);
}
public static void fun2(Map<String, ? extends Object> map) {
// 只做限定使用
//Map<String, ? extends Object>是Map<String, String>的父类型
System.out.println(map);
}
public static void fun3(Map<String, ?> map) {
// 不限定
System.out.println(map);
}
}
就像注释中说到的一样,使用?
来作为通配符,在程序里面不能声明一样类型的变量。
一个限制:
当我们进行赋值时,看下面代码:
public class MainTest {
static Pair<MyInterface> p;
public static void main(String[] args) {
}
public static void fun1(Pair<? extends MyInterface> pair) {
/*
* 报错,这是因为 Pair<? extends MyInterface>是父类型
* Pair<MyInterface> 是子类型,子类型可以赋值给父类型
* */
p = pair;
pair = p; // 正确
}
}
这里一定要注意!!!
与之相反,还有一个super
关键字:
public class MainTest {
static Pair<? super MyInterface> p;
public static void main(String[] args) {
}
public static void fun1(Pair<MyInterface> pair) {
/*
* 正确,Pair<? super MyInterface>是父类型
* Pair<MyInterface> 是子类型,子类型可以赋值给父类型
* */
p = pair;
}
}
其中:
<? super MyInterface>
表示MyInterface
的所有超类型,所以,Pair<MyInterface>
是Pair<? super MyInterface>
的子类型
- 这里其实不是很好理解,
<? super MyInterface>
其实更应该写成<MyInterface extends ?>
,只不过Java不支持这种写法,意思是一样的,便于理解。
super
关键字表示的方式叫做通配符的超类型限定,与extends
正好相反。