内容简介:Java泛型详解
泛型是 Java 5引入的机制, 允许编写不关心具体类型的类或方法. 泛型最著名的应用是 Collection
框架.
List<String> list = new ArrayList<>();
泛型类 List
在定义时并不关心元素类型, 只有在实例化时才获得具体的元素类型.
泛型类
声明泛型需要使用 <>
声明类型参数, 如 <T>
, <T1, T2>
. 下面的示例中声明了一个 Holder
类, 它的item域可以存储任意类型的引用.
class Holder<T> { T item; publicvoidset(T t) { item = t; } public T get() { return item; } publicstaticvoidmain(String[] args) { Holder<String> holder = new Holder<>(); holder.set("Hello World"); System.out.println(holder.get()); } }
在Java 7之前必须在创建实例时指定类型参数:
Holder<String> holder = new Holder<String>();
Java 7提供了类型推断功能, 可以根据声明推断实例的类型参数:
Holder<String> holder = new Holder<>(); // 不必在new对象时再写一次类型参数了
或者在 return
时推断类型:
publicstatic Holder<String> getInstance() { return new Holder<>(); }
示例中的 Holder
类没有对类型参数T做任何限制, T可以实例化为任意类型. Java允许我们限制对其进行限制.
-
<T extends MyClass>
: T必须为MyClass
或其子类 -
<T super MyClass>
: T必须为MyClass
或其父类
比较常用的是 extends
限定, 因为子类必然定义了父类的方法(和域), 因此我们可以安全地访问父类声明的方法(和域).
下面的示例中访问 item.length()
是安全的, 因为 T
的基类 String
定义了该方法.
class Holder<T extends String> { T item; publicvoidset(T t) { item = t; } public T get() { return item; } publicintlength() { return item.length(); } publicstaticvoidmain(String[] args) { Holder<String> holder = new Holder<>(); holder.set("Hello World"); System.out.println(holder.length()); } }
泛型方法
Java也允许只为方法而非整个类声明类型参数:
public class Main { public static <T> voidlog(T t) { System.out.println(t); } publicstaticvoidmain(String[] args) { log(1); } }
类型通配符
在上文示例中, 我们总是在实例化泛型类时指定具体的类代替类型参数, 比如 Holder<String>
使用 String
代替类型参数 T
.
类型通配符允许在实例化泛型类时不指定具体类:
Holder<?> holder = new Holder<>();
上述示例初始化了一个没有类型限制的 Holder
实例. 无任何限制的类型通配符可以被省略:
Holder holder = new Holder();
除非因为绝对必要的原因, 否则不建议使用无限制的类型通配符. 类型通配符同样可以使用 extends
和 super
进行范围限定.
Holder<? extends String> holder = new Holder<>();
上述示例中, holder
的 set
方法不能正常编译. 类型通配符通常用于声明方法的参数类型:
class Holder<T> { T item; publicvoidset(T t) { item = t; } public T get() { return item; } publicstaticvoidprint(Holder<? extends String> holder) { System.out.println(holder.get()); } publicstaticvoidmain(String[] args) { Holder<String> holder = new Holder<>(); holder.set("Hello World"); print(holder); } }
类型擦除
java的泛型采用运行时类型擦除的方式实现泛型, 也就是说类型参数仅存在于编译期, 运行时虚拟机并不知道泛型参数的存在.
publicstaticvoidmain(String[] args) { List<String> strings = new ArrayList<>(); List<Long> longs = new ArrayList<>(); System.out.println(strings.getClass() == longs.getClass()); }
上文示例输出 true
, 说明了运行期无法访问类型参数. 泛型是通过编译时添加了类型检查和自动转型的字节码来实现的.
数组也受到了类型擦除影响:
public class Main { public static <T> voidprint(T[] arr) { for (T t : arr) { System.out.println(t); } } publicstaticvoidmain(String[] args) { String[] strings = {"a", "b", "c", "d"}; print(strings); } }
上面的代码是可以正常运行的, 但是Java禁止直接创建泛型数组:
public static <T> voidtest() { T[] arr = new T[5]; }
禁止创建泛型数组的原因可以在 Java Language Specification
中窥见端倪:
在 10.6 数组初始化 中提到
[It is a compile-time error if the component type of the array being initialized is not reifiable(4.7)
看一下reifiable(物化)的 定义 :
A type is reifiable if and only if one of the following holds:
It refers to a non-generic class or interface type declaration.
It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
It is a raw type (§4.8).
It is a primitive type (§4.2).
It is an array type (§10.1) whose element type is reifiable.
因为类型擦除的原因, java.util.ArrayList
采用了 Object[]
来存储元素.
本文永久更新链接地址 : http://www.linuxidc.com/Linux/2018-01/150360.htm
以上所述就是小编给大家介绍的《Java泛型详解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Flutter 完整开发实战详解(十六、详解自定义布局实战)
- 数据结构 1 线性表详解 链表、 栈 、 队列 结合JAVA 详解
- 详解Openstack环境准备
- Java泛型详解
- iOS RunLoop 详解
- Raft协议详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。