泛型
Java集合有一个缺点就是把一个对象”丢进去“之后。集合就会”忘记“这个对象的数据类型,当再次取出该对象时,该对象的变异类型就会变成Object类型(运行时类型不变)。
这样设计的原因就是为了提高集合的通用性,但是会带来下面两个问题:
1.集合元素类型无限制,可能引起异常,不同的对象都可以放入集合,就会以引起异常。
2.由于集合对象放入集合时,集合对象就会丢失对象的状态信息,集合只知道它盛装的是Object,因此取出集合元素后通常还需要进行强制类型转换。不仅提高编程复杂度,而且可能引起ClassCastException。
import java.util.ArrayList;
import java.util.List;
public class ListErr {
public static void main(String[] args) {
//创建一个只保存字符串的List集合
List strList = new ArrayList();
strList.add("Home");
strList.add("School");
//向集合中加入一个Integer类型
strList.add(12);
//遍历集合
for (Object str : strList){
System.out.println((String)str);
}
}
}
这样就会出现一个ClassCastException异常
如果使用泛型的话就不会出现上述异常,如果你非要向集合中加入一个Integer类型时就会直接编译报错,修改代码如下
import java.util.ArrayList;
import java.util.List;
public class ListErr {
public static void main(String[] args) {
//创建一个只保存字符串的List集合
List<String> strList = new ArrayList<>();
strList.add("Home");
strList.add("School");
//向集合中加入一个Integer类型
//strList.add(12);//编译报错
for (String str : strList){
System.out.println(str);
}
}
}
泛型语法
泛型的语法如下
在Java7以前,如果使用带泛型的接口、类的定义的变量,那么构造器创建对象时构造器后必须要加泛型,这就显得有点多余了,所以在Java7以后构造器后的泛型可以省略。
List<String> strList = new ArrayList<>();
泛型接口、类
泛型类
当一个类要操作的引用数据类型不确定的时候,可以给该类定义一个形参。用到这个类的时候,通过传递类型参数的形式,来确定要操作的具体的对象类型。在JDK1.5之前,为了提高代码的通用性,通常把类型定义为所有类的父类型:Object,这样做有两大弊端:
- 在具体操作的时候要进行强制类型转换;
- 这样还是指定了类型,还是不灵活,对具体类型的方法未知且不安全。
如下:
public class MyClass {
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
但是如果我们用泛型来定义的话就不会有这些问题
public class MyClass2<M> {
private M m;
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
}
泛型接口
将泛型原理用于接口实现中,就是泛型接口。
泛型接口的格式:泛型接口格式类似于泛型类的格式,接口中的方法的格式类似于泛型方法的格式。
定义一个泛型接口
public interface MyInterface <T>{
public T read(T t);
}
使用这个泛型接口
public class Test4 implements MyInterface<String>{
public static void main(String[] args) {
Test4 t = new Test4();
System.out.println(t.read("Hello interdace"));
}
@Override
public String read(String str) {
return str;
}
}
泛型方法
泛型方法也是为了提高代码的重用性和程序安全性。编程原则:尽量设计泛型方法解决问题,如果设计泛型方法可以取代泛型整个类,应该采用泛型方法。
泛型方法的格式:类型变量放在修饰符后面和返回类型前面, 如:public static E getMax(T… in)
public class Max {
public static void main(String[] args) {
System.out.println(getMax(1,2));
System.out.println(getMax(0.3,0.5));
}
public static<T> T getMax(T t1,T t2){
return t1.equals(t2)? t1: t2;
}
}
泛型通配符
当操作的不同容器中的类型都不确定的时候,而且使用的元素都是从Object类中继承的方法,这时泛型就用通配符“?”来表示。
泛型的通配符:“?” 相当于 “? extends Object”
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class AllCollectionIterator {
public static void main(String[] args) {
HashSet<String> s1 = new HashSet<String>();
s1.add("sss1");
s1.add("sss2");
s1.add("sss3");
ArrayList<Integer> a1 = new ArrayList<Integer>();
a1.add(1);
a1.add(2);
a1.add(3);
a1.add(4);
printAllCollection(a1);
System.out.println("-------------");
printAllCollection(s1);
}
public static void printAllCollection(Collection<?> c){
Iterator<?> iter = c.iterator();
while (iter.hasNext()) {
System.out.println(iter.next().toString());
}
}
}
泛型限定
泛型限定就是对操作的数据类型限定在一个范围之内。限定分为上限和下限。
向上限定:? extends E 接收E类型或E的子类型
向下限定:? super E 接收E类型或E的父类型
限定用法和泛型方法,泛型类用法一样,在“<>”中表达即可。
一个类型变量或通配符可以有多个限定,多个限定用“&”分隔开,且限定中最多有一个类,可以有多个接口;如果有类限定,类限定必须放在限定列表的最前面。如:T extends MyClass1 & MyInterface1 & MyInterface2
!
import java.util.Calendar;
import java.util.GregorianCalendar;
public class GenericGetMax {
public static void main(String[] args) {
String[] inArrStr = {"haha", "test", "nba", "basketball"};
System.out.println(GetMax.findMax(inArrStr).toString());
Integer[] inArrInt = {11, 33, 2, 100, 101};
System.out.println(GetMax.findMax(inArrInt));
GregorianCalendar[] inArrCal = {
new GregorianCalendar(2016, Calendar.SEPTEMBER, 22),
new GregorianCalendar(2016, Calendar.OCTOBER, 10)};
System.out.println(GetMax.findMax(inArrCal).toZonedDateTime());
}
}
class GetMax {
@SafeVarargs
public static <T extends Comparable> T findMax(T... in) {
T max = in[0];
for (T one : in) {
if (one.compareTo(max) > 0) {
max = one;
}
}
return max;
}
}