java语言基础2--泛型
泛型特性出来之前,大家都使用Object类型来创建一般化的类,但是它们不能以安全的方式进行工作,泛型可以安全的工作,因为不再需要显式的强制转换,此外泛型还可以扩展重用代码的能力。
基本知识
下面是一个简单的泛型例子
public class Gen<T> { T val; Gen(T t){ val = t;} public T getVal() { return val; } void showType() { System.out.println(val.getClass().getName()); } }
public class Test { public static void main(String[] args) { //T 类型将被指定为Integer 类型。实际上编译器并没有创建不同版本的Gen类,编译器只是移除泛型所有泛型信息,进行了类型替换,这个过程叫泛型擦除 Gen<Integer> gen1 = new Gen<Integer>(100); gen1.showType();//输出 java.lang.Integer System.out.println(gen1.getVal());//输出100 // gen1 = new Gen<Double>(10.0);//上面已经将gen1 指定为了Gen<Integer>类型了,不能再指定为Gen<Double> 这种检查是泛型的特点之一,保证了类型的安全 //T 类型将被指定为String 类型 Gen<String> gen2 = new Gen<String>("I am a String"); gen2.showType();//输出java.lang.String System.out.println(gen2.getVal());//输出 I am a String } }
有界泛型 语法<T extends superClass> , 或者<T extends superClass & myInterface> 这里的接口也使用extends关键字来修饰
T能传递所有类型,这当然很好,但有时候,我们需要做一个限定,比如当我们想对一组数据求平和,那么类型肯定要数值类型才行的。因此我们需要做限定。
public class Stats<T extends Number> { T[] nums; Stats(T[] param){this.nums=param;} double average() { double sum = 0.0; for(int i = 0;i<nums.length;i++) { sum +=nums[i].doubleValue(); } return sum; } }
public class Test { public static void main(String[] args) { Integer [] arr = {1,2,3,4,5}; Stats<Integer> s = new Stats<Integer>(arr); System.out.println(s.average());//输出 15.0 BigDecimal [] arr1 = {new BigDecimal("1.1"),new BigDecimal("2.2"),new BigDecimal("3.3"),new BigDecimal("4.4"),new BigDecimal("5.5")}; Stats<BigDecimal> s1 = new Stats<BigDecimal>(arr1); System.out.println(s1.average());//输出16.5 String[] arr2 = {"1.1","2.2"}; // Stats<String> s2 = new Stats<String>(arr2);//String 不是Number类型 ,因此编译失败 } }
通配符 ?
有时候我们有以下需求,比较两个数组的求和是否相等,Integer[] arrr1={1,2,3} 和 Double arr2={1.0,2.0,3.0};期望返回true;于是我们可能会像下面这样写
public class Stats<T extends Number> { T[] nums; Stats(T[] param){this.nums=param;} double average() { double sum = 0.0; for(int i = 0;i<nums.length;i++) { sum +=nums[i].doubleValue(); } return sum; } boolean sameAverage(Stats<T> t) { return this.average()==t.average(); } }
public class Test { public static void main(String[] args) { Integer [] arr = {1,2,3,4,5}; Stats<Integer> s = new Stats<Integer>(arr); System.out.println(s.average());//输出 15.0 BigDecimal [] arr1 = {new BigDecimal("1.1"),new BigDecimal("2.2"),new BigDecimal("3.3"),new BigDecimal("4.4"),new BigDecimal("5.5")}; Stats<BigDecimal> s1 = new Stats<BigDecimal>(arr1); System.out.println(s1.average());//输出16.5 if(s.sameAverage(s1.average())) {//这里会编译失败 ,因为调用者是Stats<Integer>类型的 ,而比较对象是Stats<BigDecimal>,类型不兼容,导致编译失败 System.out.println("求和相同"); }else { System.out.println("求和不同"); } } }
为了将 sameAverage方法 泛型化,我们需要使用通配符?,表示未知类型,修改以上代码,即可通过编译
boolean sameAverage(Stats<?> t) {//这里 Stats<?>和所有Stats对象匹配 return this.average()==t.average(); }
有界通配符 语法<? extneds superClass> 上界,<? super subClass> 下界(注意与<T extneds superClass>的区别)
泛型方法
public class Generic { static <T extends Comparable<T>,V extends T> boolean isIn(T x,V[] arr) { boolean flag = false; for(int i=0;i<arr.length;i++) { if(x.equals(arr[i])) { flag=true; break; } } return flag; } public static void main(String[] args) { Integer[] arr1 = {1,2,3,4}; System.out.println(isIn(3, arr1));//输出true String[] arr2 = {"a","b","c"}; System.out.println(isIn("a", arr2));//输出true } }
static <T extends Comparable<T>,V extends T> boolean isIn(T x,V[] arr)
泛型方法 语法格式是将参数置于返回类型之前,这里是T继承Comparable<T>,同时V extends T的限制,返回类型前的<>的内容仅仅是对类型做一个限制
也可以将构造方法泛型化
public class GenCons { private double val; <T extends Number> GenCons(T t) { this.val = t.doubleValue(); } public double getVal() { return val; } public void setVal(double val) { this.val = val; } public static void main(String[] args) { GenCons con1 = new GenCons(100); System.out.println(con1.getVal()); GenCons con2 = new GenCons(100.99); System.out.println(con2.getVal()); } }
泛型接口,
public interface MinMax<T extends Comparable<T>> { T min(); T max(); }
public class MyClass<T extends Comparable<T>> implements MinMax<T> { T[] vals; MyClass(T[] vals){ this.vals=vals; } @Override public T min() { T min = vals[0]; for(int i=0;i<vals.length;i++) { if(vals[i].compareTo(min)<0) { min=vals[i]; } } return min; } @Override public T max() { T max = vals[0]; for(int i=0;i<vals.length;i++) { if(vals[i].compareTo(max)>0) { max=vals[i]; } } return max; } public static void main(String[] args) { Integer[] arr1 = {1,2,3,4}; MyClass<Integer> m1 = new MyClass<>(arr1); System.out.println(m1.min());//输出 1 System.out.println(m1.max());//输出 4 String[] arr2 = {"a","b","c","d"}; MyClass<String> m2 = new MyClass<>(arr2); System.out.println(m2.min());//输出 a System.out.println(m2.max());//输出 d } }
看一下泛型接口的声明interface MinMax<T extends Comparable<T>> 和普通类差不多
看一下实现类class MyClass<T extends Comparable<T>> implements MinMax<T>,因为MyClass实现MinMax 所以界限和接口的界限一致,此外这个界限不需要在implements 子句中指定,参数可以原封不动的传递给接口,一般来说 一个类实现泛型接口,那么这个类也需要是泛型化的比如以下方式就是错误的
public class MyClass implements MinMax<T>//错误 T类型未知
因为没有声明参数,所以无法传递类型参数给接口,编译器会报 T类型未知的错误
当然,如果实现的是一个具体的泛型接口,那么类是无需泛型化的,如以下是合法的
public class MyClass implements MinMax<Integer>
泛型类的继承的时候,也可以指定为子泛型类设置多个参数,如下
public class Gen<T> { T ob; public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } }
//这里的T 将传递给Gen,V将作为自己的类型 public class Gen2<T,V> extends Gen<T> { V v; public Gen2(V v) { super(); this.v = v; } }
非泛型类也可以作为泛型类的父类 如下
class Gen<T> extends NonGen{ }
使用instanceof 在运行时对泛型进行类型判断
public class Gen<T> { T ob; public Gen(T ob) { super(); this.ob = ob; } public T getOb() { return ob; } }
public class Gen2<T> extends Gen<T> { public Gen2(T o) { super(o); } public static void main(String[] args) { Gen<Integer> g1 = new Gen<Integer>(10); System.out.println(g1 instanceof Gen<?>);//输出true Gen2<Integer> g2 = new Gen2<Integer>(20); System.out.println(g2 instanceof Gen<?>);//输出true // System.out.println(g2 instanceof Gen2<Integer>);//编译失败 不能讲g2 与特定的类型比较 Gen<Integer> test = (Gen<Integer>) g2;//也可以对泛型进行强制转换 Gen<String> g3 = new Gen<String>("Hello"); System.out.println(g3 instanceof Gen2<?>);//输出fasle } }
泛型擦除:编译java代码时,所有的泛型信息将被移除,意味着使用它们的界定类型替换类型参数,如果没有显式的指定类型,将使用Object代替。
使用泛型的注意事项
public class Gen<T> { // static T test; // 不能静态成员不能使用 泛型参数 T ob; public Gen(T ob) { // ob = new T(); // T只是占位符 不能实例化, 因为jvm不知道T是哪种类型 } public T getOb() { return ob; } }
暗示
posted on 2018-10-14 20:33 Advance_Man 阅读(181) 评论(0) 编辑 收藏 举报