正规设计兼职网站有哪些,四平网站建设哪家好,wordpress配置文件数据库,模板小程序什么是泛型#xff1f;
泛型其实就参数化类型#xff0c;也就是说这个类型类似一个变量是可变的。
为什么会有泛型#xff1f;
在没有泛型之前#xff0c;java中是通过Object来实现泛型的功能。但是这样做有下面两个缺陷#xff1a; 1 获取值的时候必须进行强转 2 没有…什么是泛型
泛型其实就参数化类型也就是说这个类型类似一个变量是可变的。
为什么会有泛型
在没有泛型之前java中是通过Object来实现泛型的功能。但是这样做有下面两个缺陷 1 获取值的时候必须进行强转 2 没有错误检查
Object data[] new Object[]{one, two, 1, new Object()};
String str (String) data[0];一般来说我们会把相同类型的数据放到一起但是有没有发现如果使用object我们可以放入任意类型的数据编译器也不会报错这样在使用的时候就增加了类型转换异常的概率。 那么使用泛型呢
ListString strList new ArrayList();
strList.add(one);
// 这句代码编译器就会提醒你不能这样使用
strList.add(1);非static的内部类在外部类加载的时候并不会加载它所以它里面不能有静态变量或者静态方法。
泛型擦除
泛型信息只存在于代码编译阶段但是在java的运行期(已经生成字节码文件后)与泛型相关的信息会被擦除掉专业术语叫做类型擦除。
// 这个例子 最后输出的结果为 true class的结果为:java.util.ArrayList
public class PairT {public static void main(String[] args) {ListInteger integerList new ArrayList();ListString stringList new ArrayList();System.out.println(Objects.equals(integerList, stringList));System.out.println(integerList.getClass());System.out.println(stringList.getClass());}
}再看下面这个例子
public class PairT {private T data;public static void main(String[] args) {PairString stringPair new Pair();System.out.println(stringPair.getClass());Field[] declaredFields stringPair.getClass().getDeclaredFields();Arrays.stream(declaredFields).forEach(f - System.out.println(String.valueOf(f.getName() f.getType())));System.out.println();PairInteger integerPair new Pair();System.out.println(integerPair.getClass());Field[] fields integerPair.getClass().getDeclaredFields();Arrays.stream(fields).forEach(f - System.out.println(String.valueOf(f.getName() f.getType())));}
}从运行结果我们可以证明在运行时类型已经被擦除为Object类型
在泛型类被类型擦除的时候之前泛型类中的类型参数部分如果没有指定上限如则会被转译成普通的Object 类型如果指定了上限如则类型参数就被替换成类型上限。 1 没有限定泛型的界限
public class PairT {private T first;private T second;
}擦除后,没有限定泛型的界限所以是Object类型
public class Pair {private Object first;private Object second;
}2 限定了泛型的界限
public class PairT extends Comparable {private T data;public static void main(String[] args) {PairString stringPair new Pair();System.out.println(stringPair.getClass());Field[] declaredFields stringPair.getClass().getDeclaredFields();Arrays.stream(declaredFields).forEach(f - System.out.println(String.valueOf(f.getName() f.getType())));System.out.println();PairInteger integerPair new Pair();System.out.println(integerPair.getClass());Field[] fields integerPair.getClass().getDeclaredFields();Arrays.stream(fields).forEach(f - System.out.println(String.valueOf(f.getName() f.getType())));}
}这就证明在擦除后
public class PairT extends Comparable Serializable {private Comparable first;private Comparable second;
}如果交换泛型的顺序 PairT extends Serializable Comparable 那么擦除以后的类型为Serializable这个时候编译器会插入强制类型转换也就是说我们获取Comparable 类型时候会强制转换为了提高效率一般将标记接口往末尾放。
public class PairT extends Serializable Comparable {private T data;public static void main(String[] args) {PairString stringPair new Pair();System.out.println(stringPair.getClass());Field[] declaredFields stringPair.getClass().getDeclaredFields();Arrays.stream(declaredFields).forEach(f - System.out.println(String.valueOf(f.getName() f.getType())));System.out.println();PairInteger integerPair new Pair();System.out.println(integerPair.getClass());Field[] fields integerPair.getClass().getDeclaredFields();Arrays.stream(fields).forEach(f - System.out.println(String.valueOf(f.getName() f.getType())));}
}所谓的插入强制类型转换就是编译器在编译泛型表达式的时候会转化为两条指令
对原始方法的调用得到Object将返回的Object类型强制转换为具体的类型。
3 泛型方法的擦除 首先我们要区分一下泛型方法泛型方法。只有声明了的方法才是泛型方法泛型类中的使用了泛型的成员方法并不是泛型方法。如果单纯的在方法中使用了泛型它不是泛型方法。 泛型方法
public E E convert(T t) {
}非泛型方法
public T getT(T t) {
}public static T extends Comparable T min(T[] data) {return null;
}泛型方法擦除后
public static Comparable min(Comparable [] data) {return null;
}需要注意的是泛型方法的多态是通过桥方法实现的
public class PairT {private T time;public void setTime(T time) {this.time time;}
}
// 被擦除以后
public void setTime(Object time) {
}如果这个时候子类继承Pair,并指定了类型
class DateInterVal extends PairLocalDate {Overridepublic void setTime(LocalDate time) {}
}这个时候如果调用Pair的setTime方法由于多态其实底层是这样来实现的
setTime(setTime((LocalDate) time));总结1 虚拟机中没有类型只有普通的类和方法 2 所有的类型参数都会替换为他们的限定类型 3 合成桥方法来保持多态 4为保证类型安全必要时会插入强制类型转换
泛型方法
1 在类中的泛型方法 首先我们来区分几个定义方式看注释部分。
public class PairT extends Comparable Serializable {private T data;// 编译无法通过 因为这个方法是静态方法,所以我们不能使用T类型,但是我们可以使用E类型,因为E类型是申明的public static E E convert2E(T t) {return null;}// 在非静态方法的情况下 可以使用上面的类中定义的泛型Tpublic E E convert2E(T t) {return null;}// 注意这里我们在静态方方法申明了一个T类型,这个T和类上的T类型是没有关联的是一个全新的类型// 这个T可以和类的T是同一个类型,也可以不是同一个类型public static T T accept(T t) {return null;}
}2 泛型方法中的可变参数
public class PairT {static class Dot {private int x;private int y;public Dot(int x, int y) {this.x x;this.y y;}Overridepublic String toString() {return Dot{ x x , y y };}}// 泛型方法可变参数private static T void print(T ...es) {for (T t : es) {System.out.println(t );}}public static void main(String[] args) {Dot dot new Dot(1, 1);print(15000, 15000, dot);}
}2 静态方法与泛型 一个基本原则如果要在静态方法使用泛型的话这个方法必须为泛型方法。
// 也就是说必须申明
public static T T accept(T t) {return null;
}泛型缺陷
1 不能使用基本类型实例化类型参数 也就是说没有
Pairint Pairdouble 类型因为泛型在擦除以后为object类型但是原生类型不能直接赋值为object,而是要使用包装类。 2 不能实例化类型参数 本质也是因为类型擦除导致的
String string new T();
// 类型擦除以后,很显然是存在问题的
String string new Object();但是我们可以通过反射来创建一个实例 PairString pair Pair.class.newInstance();3 运行时类型查询只适用于原始类型 下面这三条语句都是编译会报错的因为虚拟中的对象总是一个特定的非泛型类型所以类查询只能查询原始类型。
pair instanceof String;
pair instanceof PairString;
pair instanceof PairT;
// 这条语句是可以的 原始类型的查询
pair instanceof Pair并且下面的语句也会返回true:
PairString ps new Pair();
PairDouble pd new Pair();
System.out.println(ps.getClass() pd.getClass());4 不能创建参数化类型的数组 类型擦除以后变为Pair[] pars new Pair[10]; 然后我们可以赋予pairs[0] new Pair(); 没有编译错误但存在运行时错误。
// 可以申明
PairString [] pairs;
// 不可以实例化 也是一样的如果把String擦除 为Object 可能会导致运行时异常 不安全
Pair[] pairs new PairString[10];
// 如果是通配类型的 则可以 但是这样的话不是很安全因为里面可以存 PairString 也可以存 PairDouble
// 在使用的时候可能类型转换异常
Pair[] pairs new Pair?[10];5 Varargs 警告
public static T void addAll(CollectionT coll, T... ts){// 这里其实创建了数组就违背了不能创建数组for(T t : ts) {coll.add(t);}
}
public static void main(String[] args) {CollectionPairString table new ArrayList();PairString pair1 new Pair();PairString pair2 new Pair();addAll(table, pair1, pair2);
}
static class PairT {}6 不能实例化类型变量 T[] array new T[10] 类型擦除后上述定义变为Object[] array new Object[10]; 这样一来我们可以将任何类型赋予array[0], 比如array[0] “1”; 编译器不会报错但运行时在使用的时候就有可能会出错了。
// 编译不会通过
T t new T();
// 编译不会通过
T[] array new T[10]这里也可以通过反射来进行
public T[] demo() {T data[] (T[]) Array.newInstance(Pair.class, 2);return data;
}7 泛型类的静态上下文中类型变量无效 也就是静态不能和泛型一起使用如果一定要一起使用的话必须申明。
// 通不过
public static T t;
// 如果一定要使用则需要申明
public static T void addAll(CollectionT coll, T... ts){for(T t : ts) {coll.add(t);}}8 不能抛出或捕获泛型类的实例
public static T extends Throwable void doWork() {try {// 会产生编译错误} catch (T e) {}
}泛型中的继承
继承泛型类时必须对父类中的类型参数进行初始化 1 使用泛型初始化父类的泛型
public class BarT extends FooT {}2 使用具体的类型
public class Bar extends FooString {}特别注意
// 这里的继承关系是 Integer 和 Double 继承
BoxNumber box new BoxNumber();
box.add(new Integer(10));
box.add(new Double(10.1));
// BoxInteger BoxNumber 他们并不是继承关系 这一定要注意 原文链接 以 Collections 类为例ArrayList 实现 List List 继承 Collection 。 所以 ArrayList 是 List 的一个子类型它是 Collection 的一个子类型。 只要不改变类型参数子类型关系在类型之间保留。
泛型中的限定
在通配符类型中允许类型参数发生变化。 1 子类限定
// 类上
class SonT extends Foo {
}
// 方法上
public T T demo2(Bar? extends Number data) {
}
// 方法的申明
public T extends Integer T demo3(Bar? super Integer data) {
}2 超类限定
// 方法上
public T T demo1(Bar? super Integer data) {
}