Java泛型
List<String> list=new ArrayList<>();
很多集合类的底层源码都是泛型来实现的。
如果我们自己实现一个List
class MyList{ private Object[] elements=new Object[10]; private int size; public void add(Object item) { elements[size++]=item; } public Object get(int index) { return elements[index]; } }
我们在编译期间是不会发现类型转换错误的,只有在运行的时候才会报错
MyList myList=new MyList(); myList.add("A"); myList.add(1); System.out.println(myList.get(0)); System.out.println((String)myList.get(1));
Java泛型类:
class DataHolder<T>{ T item; public void setData(T t) { this.item=t; } public T getData() { return this.item; } }
Java泛型方法
前面我们介绍的泛型是作用于整个类的,现在我们来介绍泛型方法。泛型方法既可以存在于泛型类中,也可以存在于普通的类中。如果使用泛型方法可以解决问题,那么应该尽量使用泛型方法。下面我们通过例子来看一下泛型方法的使用:
class DataHolder<T>{ T item; public void setData(T t) { this.item=t; } public T getData() { return this.item; } /** * 泛型方法,这里并不一定要和泛型类的类型相同 * @param e */ public <E> void PrinterInfo(E e) { System.out.println(e); } }
Java泛型接口
//定义一个泛型接口 public interface Generator<T> { public T next(); } //泛型接口未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } }
如果泛型接口传入类型参数时,实现该泛型接口的实现类,则所有使用泛型的地方都要替换成传入的实参类型
class DataHolder implements Generator<String>{ @Override public String next() { return null; } }
Java泛型擦除
Class<?> class1=new ArrayList<String>().getClass(); Class<?> class2=new ArrayList<Integer>().getClass(); System.out.println(class1); //class java.util.ArrayList System.out.println(class2); //class java.util.ArrayList System.out.println(class1.equals(class2));//true
运行时我们传入的类型变量String和Integer都被丢掉了。Java语言泛型在设计的时候为了兼容原来的旧代码,Java的泛型机制使用了“擦除”机制。由于擦除机制的存在,我们在运行期间是获取不到任何已经声明的类型信息的,泛型擦除就是生成的字节码文件不包含泛型信息。
但它只会擦除到第一个边界,extends会给参数类型添加边界
public interface HasF { void f(); } public class Manipulator<T extends HasF> { T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } }
泛型擦除的原理:
在编译过程中,类型变量的信息是能拿到的。所以,set方法在编译器可以做类型检查,非法类型不能通过编译。但是对于get方法,由于擦除机制,运行时的实际引用类型为Object类型。为了“还原”返回结果的类型,编译器在get之后添加了类型转换。
泛型擦除所带来的缺陷:在运行期间类型的一些操作都会出问题,比如instanceof,转型,new
类型判断:
/** * 泛型类型判断封装类 * @param <T> */ class GenericType<T>{ Class<?> classType; public GenericType(Class<?> type) { classType=type; } public boolean isInstance(Object object) { return classType.isInstance(object); } } Main(){ GenericType<A> genericType=new GenericType<>(A.class); System.out.println(genericType.isInstance(new A())); System.out.println(genericType.isInstance(new B())); }
new T()因为擦除,不能确定类型,不能确定T是否包含无参构造函数
/** * 使用工厂方法来创建实例 * * @param <T> */ interface Factory<T>{ T create(); } class Creater<T>{ T instance; public <F extends Factory<T>> T newInstance(F f) { instance=f.create(); return instance; } } class IntegerFactory implements Factory<Integer>{ @Override public Integer create() { Integer integer=new Integer(9); return integer; } }
通过工厂方法加泛型方法创建实例对象
Creater<Integer> creater=new Creater<>(); System.out.println(creater.newInstance(new IntegerFactory()));
总结:
泛型定义:
类型的参数化,就是可以把类型像方法的参数那样传递。
泛型使编译器可以在编译期间对类型进行检查以提高类型安全,减少运行时由于对象类型不匹配引发的异常。
泛型的几个好处:
1.帮助我们在编译期间发现一些类型错误
2.泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型(因为泛型擦除的存在,不会保留泛型信息),对象进入和离开方法(get,set)的边界处添加类型检查和类型转换的方法
3.在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
4.效率的问题。泛型集合一旦声明了是何种数据类型的集合,就只能添加何种数据类型。添加去也不会转换成Object,它是运行时动态的获取类型参数。也就是说没有装箱和拆箱这些操作。减少了处理器的资源浪费。
总结:泛型是一种思想,也是一种技术,动态的获取参数类型,让编程更加的灵活。那么问题来了,怎么实现动态的获取参数类型呢?毫无疑问,使用反射。
ege:
Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
不会报错
Generic<String> generic1 = new Generic<>(4444);就会做泛型检查然后报错
4.通过泛型上下边界可以限定类型的范围
5.通过泛型方法减少重载时的代码重复量(如果仅仅是类型不同的话)
参考:https://juejin.im/post/5b614848e51d45355d51f792
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/10827942.html

浙公网安备 33010602011771号