未使用泛型的情况:
// 创建列表类 List list = new ArrayList(); // 添加一个类型为 String 的列表元素 list.add("hello"); // 强制转换为 String 类型,再赋值给类型为 s 的引用变量 String s = (String) list.get(0);
使用泛型的情况:
// 创建泛型类,<String> 为类型参数 List<String> list = new ArrayList<String>(); // 添加一个类型为 String 的列表元素 list.add("hello"); // 这里不需要强制类型转换 String s = list.get(0);
好处:实现通用的泛型算法,处理不同类型的集合,可以自定义类型,类型安全,便于阅读。
一个泛型类型是一个类型参数化(<类型参数>)的泛型类或接口。
public class Box { private Object object; public void set(Object object) { this.object = object; } public Object get() { return object; } }
Box 类中方法接受或返回一个对象,除了基本类型外你可以传入任何对象。编译时无法检查类是如何使用的,如果传入一个 Integer 并希望获取 Integer,但却错误传入 String 对象,那么就会导致运行错误。
泛型类型定义格式:
class name<T1, T2, ..., Tn> { /* ... */ } //T1, T2, ..., Tn为类型参数
类型参数(也成为类型变量)跟在类名称后面,类型参数放在尖括号(<>)中(T1, T2, ..., and Tn),
修改后的泛型 Box 类:
/** * Box 类的泛型版本 * @param <T> 类型的值被装箱 */ public class Box<T> { // T 表示 "类型" private T t; public void set(T t) { this.t = t; } public T get() { return t; } }
上面代码中,Box 类中的所有 Object 类型都被替换为 T 类型,类型变量可以是指定的任何非基本类型:任何类类型、任何接口类型、任何数组类型,甚至是任何其他的类型变量。
泛型技术也可以实现通用的泛型接口。
按照惯例,类型参数名为单个大写字母。这与已知的变量命名约定形成鲜明对比,这样做有充分的理由:如果没有这个约定,那么很难区分类型变量和普通类或接口名称之间的区别。
最常用的类型参数名称:
用具体的类型替换类型变量 T 就可以实例化泛型类型,例如:
Box <Integer> integerBox = new Box <Integer>();
通用类型的调用一般称为“参数化类型”。
像往常一样使用 new
关键字实例化,但在类名和括号之间放置 <Integer>
:
在 Java SE 7 和更高的版本中,只要编译器可以根据上下文确定或推断类型参数,就可以用一组空类型参数(<>)替换调用泛型类的构造函数所需的类型参数。例如:
Box<Integer> integerBox = new Box<>();
泛型类可以拥有多个类型参数。例如,实现通用 Pair 接口的通用 OrderedPair 类:
public interface Pair<K, V> { public K getKey(); public V getValue(); } public class OrderedPair<K, V> implements Pair<K, V> { private K key; private V value; public OrderedPair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } }
以下语句创建 OrderedPair 类的两个实例:
Pair <String,Integer> p1 = new OrderedPair <String,Integer>(“Even”,8); Pair <String,String> p2 = new OrderedPair <String,String>(“hello”,“world”);
新的 OrderedPair <String,Integer>
代码将 K 实例化为一个字符串,将 V 实例化为一个整数。因此,OrderedPair 的构造函数的参数类型分别是 String 和 Integer。由于自动装箱,将 String 和 int 传递给类是有效的。
正如前文所述,由于Java编译器可以从 OrderedPair <String,Integer>
声明中推断 K 和 V 类型,因此可以使用以下缩写:
OrderedPair <String,Integer> p1 = new OrderedPair <>(“Even”,8); OrderedPair <String,String> p2 = new OrderedPair <>(“hello”,“world”);
您也可以用参数化类型(即List )替换类型参数(即 K 或 V)。例如,使用 OrderedPair <K,V>
示例:
OrderedPair <String,Box <Integer>> p = new OrderedPair <>(“primes”,new Box <Integer>(...));
原始类型是没有任何类型参数的泛型类或接口的名称。
例如,给定一个 Box 泛型类:
public class Box<T> { public void set(T t) { /* ... */ } // ... }
要创建参数化类型,为形式类型参数 T 提供实际类型参数:
Box<Integer> intBox = new Box<>();
如果省略实际类型参数,则会创建一个 Box<T>
的原始类型:
Box rawBox = new Box();
因此,Box 是泛型 Box<T>
的原始类型。但是,非泛型类或接口类型不是原始类型。
在 JDK5.0 之前很多 API 类(如 Collections 类)不是通用的,为了向后兼容,允许将参数化类型分配给其原始类型:
Box<String> stringBox = new Box<>(); Box rawBox = stringBox; // OK
但是,将原始类型分配给参数化类型,编译器会发出一个警告:
Box rawBox = new Box(); // rawBox 是 Box<T> 的原始类型 Box<Integer> intBox = rawBox; // warning: unchecked conversion
如果使用原始类型来调用相应泛型类型中定义的泛型方法,则还会收到警告:
Box<String> stringBox = new Box<>(); Box rawBox = stringBox; rawBox.set(8); // warning: unchecked invocation to set(T)
该警告显示原始类型绕过了泛型类型检查,将不安全代码的捕获推迟到运行时。因此,你应该避免使用原始类型。
Linux公社的RSS地址 : https://www.linuxidc.com/rssFeed.aspx
本文永久更新链接地址: https://www.linuxidc.com/Linux/2019-06/158977.htm