泛型
泛型是java1.5之后才引入的,为什么要引入泛型:
1 为了使类型参数化
2 提高类型的安全性并简化类型转换的过程,在泛型处理过程中,类型的转换都是自动和隐士的,泛型让类型检查从运行期提前到编译器,出错能预先提示。
在没有泛型的情况:
class TestObj{ private Object obj = null; public TestObj(Object obj) { this.obj = obj; } public void speak() { System.out.println(this.obj.getClass()); } public Object getObj() { return obj; } } Integer i1 = new Integer(10); TestObj to = new TestObj(i1); to.speak(); Integer i2 =(Integer)to.getObj(); System.out.println(i2);
在这里Integer i2 =(Integer)to.getObj(); 类型转换是不安全的。如果不能确认object类型是integer 那么就会出错。
在看看泛型:
class GenObj <T>{ private T obj; public GenObj(T obj){ this.obj = obj; } public void speak() { System.out.println(obj.getClass()); } public T getObj() { return (T)obj; } } Integer i1 = new Integer(10); //GenObj go = new GenObj(i1); GenObj<String> go = new GenObj<String>("123"); go.speak();
如果用GenObj go = new GenObj(i1); 来实例化引入泛型的对象会出现警告:
提示说要对通用类型的引用参数化,这里参数化就是<>里的,这句话就是开头说的为了使类型参数化;
引入的泛型之后就不用使用显示的强制类型转换了。
Integer i1 = new Integer(10); //GenObj go = new GenObj(i1); GenObj<String> go = new GenObj<String>("123"); go.speak(); String st1 = go.getObj(); System.out.println(st1);
如果类型错误,编译器会报错。
泛型参数T 必须是引用类型,不能基本数据类型(int long char等等);
多个参数的泛型:
class MutilGen<K,V>{ private K k1; private V v1; public MutilGen(K k1,V v1) { this.k1 = k1; this.v1 = v1; } public K getK1() { return k1; } public V getV1() { return v1; } public void speak() { System.out.println(this.k1.getClass()+":"+this.k1.toString()); System.out.println(this.v1.getClass()+":"+this.v1.toString()); } } MutilGen<String, Integer> mg = new MutilGen<String, Integer>("你好", 1); mg.speak();
执行结果是:
class java.lang.String:你好
class java.lang.Integer:1
多参数用逗号隔开。
泛型的类型边界:
为什么要有类型边界呢,看下面这个程序。
public class SumTest<M> { M[] tsMs; double sum=0; public SumTest(M[] tsMs) { this.tsMs = tsMs; } public double Sum() { for(int i=0;i<tsMs.length;i++) { sum +=tsMs[i].doubleValue(); } return sum; } }
这里tsMs[i].doubleValue();会报错,为什么呢,因为定义的泛型参数M 编译器并不知道是什么类型,所以这里用doubleValue()肯定是不行的。如果我们确认传入的参数一些数据类型,也就是说这些参数是在一个范围的,有点像c语言的枚举类型,将泛型参数的边界确定下来,看修改之后的程序:
public class SumTest<M extends Number> { M[] tsMs; double sum=0; public SumTest(M[] tsMs) { this.tsMs = tsMs; } public double Sum() { for(int i=0;i<tsMs.length;i++) { sum +=tsMs[i].doubleValue(); } return sum; } public static void main(String[] args) { Integer[] intArr = new Integer[3]; ArrayList<Integer> intList = new ArrayList<Integer>(); intList.add(100); intList.add(10); intList.add(200); intArr = intList.toArray(new Integer[0]); SumTest<Integer> st = new SumTest<Integer>(intArr); System.out.println(st.Sum()); } }
结果是310.0
因为数据类型 Byge Double Long Integer Short的父类是Number,所以用externds 来确定泛型M的边界,我感觉非常类似与c语言里的枚举类型,举个例子:
typedef enum{ Monday = 0, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }Week; void ShowDay(Week day){ switch(day){ case 0:printf("Monday\r\n");break; case 1:printf("Monday\r\n");break; case 2:printf("Tuesday\r\n");break; case 3:printf("Wednesday\r\n");break; case 4:printf("Thursday\r\n");break; case 5:printf("Saturday\r\n");break; case 6:printf("Sunday\r\n");break; default:break; } }
ShowDay(Monday);
通配符:?
M[] tsMs; double sum=0; public SumTest(M[] tsMs) { this.tsMs = tsMs; } public double Sum() { for(int i=0;i<tsMs.length;i++) { sum +=tsMs[i].doubleValue(); } return sum; } public boolean SumEqual(SumTest<M> st) { if(this.Sum() == st.Sum()) { return true; }else { return false; } } public static void main(String[] args) { Integer[] intArr = {1,2,3}; Integer[] intArr1 = {1,2,3}; Double[] dblArr = {1.0,2.0,3.0}; SumTest<Integer> st = new SumTest<Integer>(intArr); SumTest<Integer> st1 = new SumTest<Integer>(intArr1); SumTest<Double> st2 = new SumTest<Double>(dblArr); System.out.println(st.SumEqual(st1)); System.out.println(st.SumEqual(st2));//这里会报错
}
这里加入一个public boolean SumEqual(SumTest<M> st) 方法,传入的是参数是SumTest,如果按照这样定义会出现参数不匹配,这里很好理解,因为st1传入的是Integer,而st2是Double类型,那么怎么解决这个问题呢。将泛型参数<M>改成通配符<?>即可。
public boolean SumEqual(SumTest<?> st) { if(this.Sum() == st.Sum()) { return true; }else { return false; } }
结果是:
true
false