版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ccnuacmhdu/article/details/82936747
把对象放入Java集合中,编译的时候都会当成Object类型(运行时还是原来的实际类型),各种对象都可以放入集合,但是带来的问题是从集合中取对象的时候可能要进行强制类型转换了。如果集合中放了各种对象,特别是集合元素没有顺序的话,程序员只知道当初放进去很多类型的对象,取出的时候却不知道哪个对象是什么类型的了,不知道要强转成什么类型,结果是无法取出或者硬转成可能错误的类型报异常。使用泛型的集合就是说,在定义一个集合的时候就指定这个集合中必须放置哪种类型的对象。 泛型可以在集合中使用,在其他方面也可以使用。
自定义使用泛型的类、接口
class A<T>{
private T x;
public A(T x){
this.x = x;
}
public T getX(){
return this.x;
}
}
public class Hello{
public static void main(String[] args){
A<String> a1 = new A<String>("abc");
System.out.println(a1.getX());//abc
//注意用Integer而不是int,必须是引用类型
A<Integer> a2 = new A<>(2333333);//A<>这个尖括号省略了Integer就是所谓的“菱形”
System.out.println(a2.getX());//2333333
}
}
在上面的基础上,如果有子类继承自定义泛型的父类的话:
//这样没问题
class B extends A{
public Object getX(){
return "子类";
}
}
//这样是错误的,覆盖父类的方法,下面的Object必须换成String
class B extends A<String>{
public Object getX(){
return "子类";
}
}
静态变量、静态方法不得使用泛型。不管泛型指定何种引用类型,类或接口的类型不变
ArrayList<String> al1 = new ArrayList<>();
ArrayList<Integer> al2 = new ArrayList<>();
System.out.println(al1.getClass() == al2.getClass());//true
类型通配符
Java泛型的事迹原则是,只要在编译阶段没有出现警告,运行时就不会抛出ClassCastException异常。
//这段代码是错误的,一旦指明泛型,必须严格一致。如果传入的是存放String类型的List就报错抛异常
//参数应该改成List list,不要指定泛型
public void test(List<Object> list){
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
类型通配符初步?
//这样没问题的!下面的?是类型通配符,可以匹配任何类型。
public void test(List<?> list){
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
设定类型通配符的上界
//必须是A类型及A的子类类型
List<? extends A>
import java.util.ArrayList;
class A{
public void fun(){
System.out.println("fun_A");
};
}
class B extends A{
public void fun(){
System.out.println("fun_B");
}
}
class C extends A{
public void fun(){
System.out.println("fun_C");
}
}
public class Hello{
//指定上界的通配符,可以传入A类型及A类型的子类
public void fun(ArrayList<? extends A> list){
for(A a : list){
a.fun();
}
//注意,这里不得往list里面添加B或C,因为list的类型不确定
//list.add(new B());
}
public static void main(String[] args){
ArrayList<A> list = new ArrayList<>();
list.add(new C());
list.add(new B());
list.add(new A());
new Hello().fun(list);
}
}
设定类型通配符的下界
//必须是A类型及A的父类类型(super:超过)
List<? super A>
import java.util.ArrayList;
import java.util.Collection;
public class Hello{
//把src集合的元素复制到dest集合。这就要求src集合和dest集合元素类型相同或者
//src集合元素是dest集合元素的子类
public static <T> T copy(Collection<? super T> dest, Collection<T> src){
T last = null;
for(T ele : src){
last = ele;
dest.add(ele);
}
return last;
}
public static void main(String[] args){
//Integer extends Number
ArrayList<Number> aln = new ArrayList<>();
ArrayList<Integer> ali = new ArrayList<>();
ali.add(6);
Integer last = copy(aln, ali);
System.out.println(last);
}
}
设定通配符下界的具体案例可以查看官方APITreeSet,TreeMap的带有Comparator参数的构造器。具体示例代码见355页~356页。
泛型方法
笔者认为书上所说的泛型方法和类型通配符两种方法实现泛型,其实如出一辙,无需区别,本质相同。抓住关键一点,子类可以复制给父类的引用变量。
import java.util.ArrayList;
import java.util.Collection;
public class Hello{
public static <T> void fromArrayToCollection(T[] a, Collection<T> c){
for(T o : a){
c.add(o);
}
}
public static void main(String[] args){
String s[] = new String[10];
Collection<String> cs = new ArrayList<>();
fromArrayToCollection(s, cs);
Integer[] i = new Integer[10];
Collection<Number> cn = new ArrayList<>();
fromArrayToCollection(i, cn);
}
}
此程序中笔者看到了一个小小的发现:
Number n = new Number();//显然是错的,抽象类不可实例化
//这是正确的,因为右边返回的是一个指向长度为12的Number引用类型的引用变量
Number[] n = new Number[12];
Java 7的“菱形”语法与泛型构造器
class A{
public <T> A(T t){
System.out.println(t);
}
}
public class Hello{
public static void main(String[] args){
//根据传入的参数推断泛型类型
new A(3);//T:Integer
//根据传入的参数推断泛型类型
new A("hello");//T:String
new <String> A("hihihi");
//new <String> A(4);//错误代码
}
}
泛型方法与方法重载
一个类中定义了下面两个方法(可以完成同一个操作):
public static <T> void copy(Collection<T>dest, Collection<? extends T>src){};
public static <T> void copy(Collection<? super T>dest, Collection<T>src){};
编译报错,不符合重载条件。
转换与擦除
import java.util.ArrayList;
import java.util.List;
public class Hello{
public static void main(String[] args){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
//所谓的擦除
List li = list;//把带有泛型的变量赋给没有泛型的,泛型信息丢失
//像List没有指定泛型,称为raw type,默认是声明该泛型形参指定的第一上限类型
//List没指定泛型的话,默认就是Object
//如果这里写成List<String>的话,再输出ls.get(0)就会抛异常,整数不能自动转为String
List<Integer> ls = li;//没泛型的变量赋给有泛型的变量,警告信息-Xlint:unchecked
System.out.println(ls.get(0));//运行正确
}
}
泛型与数组
关于数组元素类型不能包含泛型变量或泛型形参(除非是无上限类型通配符:<?>)的内容见该书364~365页。