有网友碰到过这样的问题:第九章.泛型 - lanshanxiao,问题详细内容为:挺不错的博文:第九章.泛型 - lanshanxiao,我搜你通过互联网收集了相关的一些解决方案,希望对有过相同或者相似问题的网友提供帮助,具体如下:
泛型入门:
Java集合有个缺点:把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型(其运行时类型没变)。
Java集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象,所以把集合设计成可以保存任何类型的对象,只要求有很好的通用性。但这样
做带来了两个问题:
1.集合对元素类型没有任何限制,这样引发一些问题。如:想创建一个只能保存Dog对象的集合,但程序也可以轻易的将Cat对象“丢”进去,所以可能引发异常
2.由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道盛装它的是Object,因此取出集合元素后通常还需要进行强制类型转换。这种强制转换既增加了编
程的复杂度,也可能引发ClassCastException异常。
编译时不检查类型的异常:


1 import java.util.List;
2 import java.util.ArrayList;
3
4 public class ListErr{
5
public static void main(String[] args){
6
//创建一个只想保存字符串的List集合
7
List strList = new ArrayList();
8
strList.add("疯狂Java讲义");
9
strList.add("疯狂Android讲义");
10
//"不小心"把一个Integer对象"丢进"了集合
11
strList.add(5);
12
strList.forEach(str -> System.out.println(((String) str).length()));
13
}
14 }
View Code

使用泛型
从Java5后,Java引入了”参数化类型“(parameterized type)”概念,允许程序在创建集合时指定集合元素的类型。Java的参数化类型被称为泛型(Generic)


1 import java.util.List;
2 import java.util.ArrayList;
3
4 public class GenericList{
5
public static void main(String[] args){
6
//创建一个只想保存字符串的List集合
7
List<String> strList = new ArrayList<String>();
8
strList.add("疯狂Java讲义");
9
strList.add("疯狂Android讲义");
10
//"不小心"把一个Integer对象"丢进"了集合
11
strList.add(5);
12
strList.forEach(str -> System.out.println(((String) str).length()));
13
}
14 }
View Code

Java7泛型的“菱形”语法:
在Java7以前,若使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器后面也必须带泛型,这就显得有些多余:
List<String> strList = new ArrayList<String>();
Map<String, Integer> scores = new HashMap<String, Integer>();
Java8之后,可以这样写:
List<String> strList = new ArrayList<>();
Map<String, Integer> scores = new HashMap<>();
把两个尖括号并排放在一起非常像一个菱形,这种语法也被称为“菱形”语法。


1 import java.util.List;
2 import java.util.ArrayList;
3 import java.util.Map;
4 import java.util.HashMap;
5
6
7 public class DiamondTest{
8
public static void main(String[] args){
9
//Java 自动推断出ArrayList的<>里应该是String
10
List<String> books = new ArrayList<>();
11
books.add("疯狂Java讲义");
12
books.add("疯狂Android讲义");
13
//遍历books集合,集合元素就是String类型
14
books.forEach(ele -> System.out.println(ele.length()));
15
//Java自动推断出HashMap的<>里应该是String,List<String>
16
Map<String, List<String>> schoolsInfo = new HashMap<>();
17
//Java自动推断出ArrayList的<>里应该是String
18
List<String> schools = new ArrayList<>();
19
schools.add("斜月三星洞");
20
schools.add("西天取经路");
21
schoolsInfo.put("孙悟空", schools);
22
//遍历Map时,Map的key是String类型,value是List<String>类型
23
schoolsInfo.forEach((key, value) -> System.out.println(key + "-->" + value));
24
}
25 }
View Code

深入泛型:
定义泛型接口、类:
泛型就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。
可以为任何类、接口增加泛型声明(并不是只有集合类才可以使用泛型声明,虽然集合类是泛型的重要使用场所)。


1 //定义Apple类时使用了泛型声明
2 public class Apple<T>{
3
//使用T类型形参定义实例变量
4
private T info;
5
public Apple(){}
6
//下面方法中使用T类型形参来定义构造器
7
public Apple(T info){
8
this.info = info;
9
}
10
public void setInfo(T info){
11
this.info = info;
12
}
13
public T getInfo(){
14
return this.info;
15
}
16
public static void main(String[] args){
17
//由于传给T形参的是String,所以构造器参数只能是String
18
Apple<String> a1 = new Apple<>("苹果");
19
System.out.println(a1.getInfo());
20
//由于传给T形参的是Double,所以构造器参数只能是Double或double
21
Apple<Double> a2 = new Apple<>(5.67);
22
System.out.println(a2.getInfo());
23
}
24 }
View Code

当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明。如:为Apple<T>类定义构造器,其构造器名依然是Apple,而不
是Apple<T>!调用该构造器是却可以使用Apple<T>的形式,应该为T形参传入实际的类型参数。
从泛型类派生子类:
创建带泛型声明的接口、父类后,可以为该接口创建实现类或派生子类。需要注意的是:当使用这些接口、父类是不能再包含类型形参<T>:
//定义类A继承Apple类,Apple类不能跟类型形参
public class A extends Apple<T>{ }//这是错误的
定义方法时,方法中的形参数据,被称为形参或数据形参。在使用方法时,必须为这些形参传入实际的数据。
类型形参也一样,在定义类、接口、方法时可以声明类型形参如:<T>,但使用这些类、接口、方法时应该为该类型形参传入实际的类型,如:<String>。
//使用Apple类时,为T形参传入String类型
public class A extends Apple<String>{ }
也可以不为类型形参传入实际的类型:
//使用Apple类时,没有为T形参传入实际的类型参数
public class A extends Apple{ }
若从Apple<String>类派生子类,则在Apple类中所有使用T类型形参的地方都将被替换成String类型,那么它的子类将会继承到String getInfo()和void setInfo(String info)两