课时97:泛型通配符
摘要:本文围绕Java泛型展开,深入探讨其在解决对象强制转换安全隐患的同时所引发的新问题,特别是引用传递处理方面的困境。通过实际代码示例,详细介绍通配符问号及其扩展(设置泛型上限和下限)的使用方法与作用,帮助读者理解这些概念在Java编程中的重要性及应用场景。
1. 泛型引发的问题
2. 通配符问号的使用
3. 通配符的扩展:设置泛型上限和下限
01、 泛型引发的问题
Java泛型在帮助开发者解决对象强制转换带来的安全隐患的同时,也产生了新的问题,其中较为突出的是引用传递处理问题。
1.1 案例程序展示
首先看一个简单的Java程序示例。定义一个泛型类`Message<T>`:
class Message<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return this.content; } } ``` public static void main(String[] args) { Message<String> msg = new Message<>(); msg.setContent("www.mldn.cn"); fun(msg) 再编写一个测试方法`fun`: public static void fun(Message<String> temp) { System.out.println(temp.getContent()); } } }
这段代码实现了一个基本的引用传递,在这个示例中,`fun`方法仅能接收`Message<String>`类型的对象。
1.2 泛型类型兼容性问题
问题的关键在于fun()方法上,如果真的希望`fun`方法能接收任意泛型类型的`Message`对象,例如将其参数类型改为`Message<Integer>`,会出现编译错误,提示不兼容类型,`Message<Integer>`无法转换为`Message<String>`。这就暴露出了泛型在引用传递时的问题:当需要处理多种泛型类型时,代码的兼容性和灵活性受到了限制。这个时候它只能接受“message<string>”类型。
1.3 方法重载的困境
有开发者可能会想到通过方法重载来解决这个问题,例如定义多个不同参数类型的`fun`方法:
public static void fun(Message<String> temp) { // 方法体 } public static void fun(Message<Integer> temp) { // 方法体 }
然而,这种做法会导致名称冲突,因为在Java中,方法重载要求方法的参数类型或个数不同,而泛型类型在方法签名中并不被视为不同的参数类型。如果不断添加不同泛型类型的重载方法,当泛型类型众多时,代码将变得冗长且难以维护。
1.4 不使用泛型的风险
如果不设置泛型,代码虽然能够编译和执行,但会带来严重的安全隐患。例如:
public class JavaDemo { public static void main(String args[]) { Message<Integer> msgA = new Message<Integer>; Message<Integer> msgB = new Message<String>; msg.setContent(110); fun(msgA); msgB.setContent(“www.mldn.cn”); fun(msgB); } } public static void fun(Message temp) { System.out.println(temp.getContent()); }
如果我们在编译一个temp.setContent(1.1)。在这个例子中,由于没有泛型的约束,`setContent`方法可以接受任意类型的数据,这可能导致数据类型不一致的错误,在实际编程中会引发难以排查的问题。
02、 通配符问号的使用
为了解决上述问题,可以使用通配符问号(`?`)。在方法参数中使用`Message<?>`,表示该方法可以接收任意泛型类型的`Message`对象,但不能修改其内容,只能获取数据。
2.1 通配符的作用示例
修改`fun`方法如下:
public static void fun(Message<?> temp) { System.out.println(temp.getContent()); }
此时,`fun`方法可以接收不同泛型类型的`Message`对象,如`Message<String>`、`Message<Integer>`等,同时避免了因类型不兼容导致的编译错误,并且保证了数据的安全性,防止在方法内部意外修改数据。
03、 通配符的扩展:设置泛型上限和下限
3.1 设置泛型上限
使用`? extends 类型`来设置泛型上限,表示该泛型类型必须是指定类型或其子类型。例如,`? extends Number`表示泛型类型只能是`Number`或`Number`的子类。
public static void fun(Message<? extends Number> temp) { // 方法体 }
在这种情况下,如果传递给`fun`方法的`Message`对象的泛型类型不是`Number`或其子类型,将会出现编译错误。
删掉string后执行成功。
3.2 设置泛型下限
使用`? super 类型`来设置泛型下限,表示该泛型类型必须是指定类型或其父类型。例如,`? super String`表示泛型类型只能是`String`或其父类(如`Object`)。
public static void fun(Message<? super String> temp) { // 方法体 }
当传递给`fun`方法的`Message`对象的泛型类型不符合下限要求时,同样会出现编译错误。
更改msgA.setcontent(“www.mldn.cn“),再一次执行
Java泛型在解决对象强制转换安全隐患的同时,带来了引用传递处理的问题。通配符问号及其扩展(设置泛型上限和下限)为解决这些问题提供了有效的途径。
通配符问号允许方法接收任意泛型类型的对象,同时限制对其内容的修改,保证数据安全;泛型上限和下限则进一步细化了对泛型类型的约束,提高了代码的安全性和可读性。在学习和使用Java系统类库时,会频繁遇到这些概念的应用,因此深入理解并掌握它们对于Java编程至关重要。