1.Java中的泛型是什么 ? 使用泛型的好处是什么?
泛型是在编译期间能发现类型的错误,防止非定义的类型出现。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
好处:1、泛型可以使代码重复利用 2、泛型类型安全,提供编译时检查 3、性能不错
2. Java的泛型是如何工作的 ? 什么是类型擦除 ?
泛型通过类型擦除进行工作的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List<String>在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。
类型擦除: 所有类型参数都用他们的限定类型替换:比如 T->Object 和 extends BaseClass->BaseClass
List<String> l1 = new ArrayList<String>(); List<Integer> l2 = new ArrayList<Integer>(); System.out.println(l1.getClass() == l2.getClass()); //打印的结果为 true 是因为 List<String> 和 List<Integer> 在 jvm 中的 Class 都是 List.class。 //在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 <T> 则会被转译成普通的 Object 类型, //如果指定了上限如 <T extends String> 则类型参数就被替换成类型上限。
1、类型检查:在生成字节码之前提供类型检查 2、类型擦除:所有类型参数都用他们的限定类型替换,包括类、变量和方法(类型擦除) 3、如果类型擦除和多态性发生了冲突时,则在子类中生成桥方法解决(擦除类型参数后吗,就不是重写了,破坏了类的多态性,方法名相同,但是不知道调用哪个) public void setXXX(Object second) //这里就调用了 重写的 父类 (Object) {setXXX((AAA)XXX)} //A表示自定的类型用父类还是自身 4、如果调用泛型方法的返回类型被擦除,则在调用该方法时插入强制类型转换
3. 什么是泛型中的限定通配符和非限定通配符 ?
<?> 被称作无限定的通配符。 //只能调用类中与类型无关的方法,什么add啊 remove啊都不能用了 <? extends T> 被称作有上限的通配符。 //只能由T和T的子类 <? super T> 被称作有下限的通配符。 //只能由T和T的父类
4. List<? extends T>和List <? super T>之间有什么区别 ?
List<? extends T>可以接受任何继承自T的类型的List,而List<? super T>可以接受任何T的父类构成的List。
5. 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
但出于规范的目的,Java 还是建议我们用单个大写字母来代表类型参数。常见的如: 1. T 代表一般的任何类。 2. E 代表 Element 的意思,或者 Exception 异常的意思。 3. K 代表 Key 的意思。 4. V 代表 Value 的意思,通常与 K 一起配合使用。 5. S 代表 Subtype 的意思,文章后面部分会讲解示意。
public V put(K key, V value) { V v = value; return v; }
6. Java中如何使用泛型编写带有参数的类?
public class MultiType <E,T>{ E value1; T value2; public E getValue1(){ return value1; } public T getValue2(){ return value2; } }
//泛型类与泛型方法的共存现象 public class Test1<T>{ public void testMethod(T t){ System.out.println(t.getClass().getName()); } public <T> T testMethod1(T t){ return t; } } Test1<String> t = new Test1(); t.testMethod("generic"); Integer i = t.testMethod1(new Integer(1));
7. 编写一段泛型程序来实现LRU缓存?(最近最少使用)
public class LRUCache2<K, V> extends LinkedHashMap<K, V> { private final int MAX_CACHE_SIZE; public LRUCache2(int cacheSize) { super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); MAX_CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Map.Entry<K, V> entry : entrySet()) { sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue())); } return sb.toString(); } }
8. 你可以把List<String>传递给一个接受List<Object>参数的方法吗?
这样做的话会导致编译错误。因为List<Object>可以存储任何类型的对象包括String, Integer等等,而List<String>却只能用来存储Strings。
9. Array中可以用泛型吗?
Array事实上并不支持泛型,这也是为什么Joshua Bloch在Effective Java一书中建议使用List来代替Array,因为List可以提供编译期的类型安全保证,而Array却不能。
10. 如何阻止Java中的类型未检查的警告?
如果你把泛型和原始类型混合起来使用,Java 5的javac编译器会产生类型未检查的警告,例如:List<String> rawList = new ArrayList();
11.泛型的缺点
List<String> a = new ArrayList()<>; a.add("11"); a.add(11); //正常情况下,因为泛型的限制,编译器不让最后一行代码编译通过,因为类似不匹配,但是,基于对类型擦除的了解,利用反射,我们可以绕过这个限制。 public interface List<E> extends Collection<E>{ boolean add(E e); } 上面是 List 和其中的 add() 方法的源码定义。 因为 E 代表任意的类型,所以类型擦除时,add 方法其实等同于 boolean add(Object obj);
12.泛型类或者泛型方法中,不接受 8 种基本数据类型
13.Java 不能创建具体类型的泛型数组
1/不能用基本类型实例化类型参数。不接受 8 种基本数据类型int/double/..。只接受Integer/Double/...之类 2/运行时类型查询只适用于原始类型 不要用if (a instanceof Pair<String>)而是(a.getClassO == b.getClass()) 3/不能创建参数化类型的数组 Pair<String>[] table = new Pair<String>[10]; // Error 4/Varargs警告 :public static <T> void addAll(Collections coll, T... ts)产生数组违反上述3,可以使用 @SafeVarargs 标注来消除创建泛型数组的有关限制 5/不能实例化类型变置:public Pair() { first = new T(); second = new T(); } // Error。 最好的解决办法是让调用者提供一个构造器表达式:Pair<String> p = Pair.makePairCString::new); public static <T> Pair<T> makePair(Supplier<T> constr) {return new Pair<>(constr.get0. constr.get0);} 6/不能构造泛型数组:public static <T extends Comparable〉T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // Error 类型擦除会让这个方法永远构造 Comparable[2] 数组 在这种情况下,最好让用户提供一个数组构造器表达式String[] ss = ArrayAlg.minmax (String[]::new,"Tom", "Dick", "Harry") minmax 方法使用这个参数生成一个有正确类型的数组: public static <T extends Comparable〉T[] minmax(IntFunction<TD> constr, T... a){T[] mm = constr.apply(2);} 7/泛型类的静态上下文中类型变量无效,不能在静态域或方法中引用类型 8/不能抛出或捕获泛型类的实例,不过在异常规范中使用类型变量是允许的 9/可以消除对受查异常的检查 10/当泛型类型被擦除时, 无法创建引发冲突的条件,记得获取桥方法 11/S是T的子类,那么Pair<S> T> 的一个子类吗? 答案是“ 不是”,泛型没有继承,