泛型
import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class Driver { public static void main(String[] args) { List<Number> list = new ArrayList(); Number number = list.get(0); List list1 = new LinkedList<Number>(); Object o = list1.get(0); } }
二、泛型类
import java.util.ArrayList; import java.util.List; public class Driver { public static void main(String[] args) { Animal<String> animal = new Animal<>(); animal.setName("黄牛"); String name = animal.getName(); System.out.println(name); // 黄牛 } } class Animal<N> { private N name; public N getName() { return name; } public void setName(N name) { this.name = name; } }
2、子类不是泛型类,子类在继承泛型类时确定泛型类型
class Animal<N> { } class Dog extends Animal<String> { }
3、子类也是泛型类,子类在继承泛型父类时保证子类的泛型标识包含父类的泛型标识
class Animal<N> { } class Dog<T> extends Animal<T> { } class Cat<T, K> extends Animal<T> { } // 这种报错 class Tiger<E> extends Animal<T> { }
三、泛型方法
1、泛型方法特指在方法返回值前使用 <T> 标识的方法
class Demo { public <T> List<T> demo(T t) { List<T> list = new LinkedList<>(); list.add(t); return list; } }
2、调用泛型方法时确定泛型类型
import java.util.LinkedList; import java.util.List; public class Driver { public static void main(String[] args) { List<String> list = new Demo().demo("黄桃"); String s = list.get(0); System.out.println(s); } } class Demo { public <T> List<T> demo(T t) { List<T> list = new LinkedList<>(); list.add(t); return list; } }
3、泛型方法不传递参数就无法确定泛型类型;但泛型方法声明时可以没有形参,没有语法错误,此时泛型类型是Object。
import java.util.LinkedList; import java.util.List; public class Driver { public static void main(String[] args) { List<Object> demo = new Demo().demo(); } } class Demo { public <T> List<T> demo() { List<T> list = new LinkedList<>(); return list; } }
4、泛型类的成员方法不可以是静态的;泛型方法可以是静态的
class Demo<D> { private D d; // 报错 public static void setD(D d) { this.d = d; } public static <T> void demo() { } }
四、泛型符号使用位置
1、<T>或<T extends Number>,声明泛型类和泛型方法的位置只能使用这两种写法
class Demo<T> {} class Demo2<T extends Number> {} class Demo3 { <D> void demo() {} <D extends Number> void demo2() {}; }
2、T,泛型类和泛型方法内部可以把泛型符号当做类型使用
class Demo<T> { private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } } class Demo2 { <T> T demo(T t) {return null;} }
3、List<?>、List<? extends Number>、List<? super Number>可以在泛型类外部使用,用来声明变量,有三种形式:声明全局或局部变量、声明方法形参、声明方法返回值。
import java.util.List; class Demo { List<?> list; List<? extends Number> list2; List<? super Number> list3; void demo(){ List<?> list; List<? extends Number> list2; List<? super Number> list3; } void demo1(List<?> list) {} void demo2(List<? extends Number> list) {} void demo3(List<? super Number> list) {} List<?> demo4() {return null;} List<? extends Number> demo5() {return null;} List<? super Number> demo6() {return null;} }
4、List<? extends E>、List<? super E>,在一个泛型类或泛型方法内部,另一个泛型类外部使用,如下,这里的外部泛型类指的是List类。
import java.util.List; class Demo<E> { List<? extends E> list; List<? super E> list2; } class Demo2 { <D> void demo() { List<? extends D> list; List<? super D> list2; } }
五、实例化泛型
1、泛型实例化错误示例
public class Demo<T> { // 表达式右边报错 private T t = new T(); }
2、泛型实例化正确示例
class Demo<T> { T t; Demo(Class<T> clazz) throws InstantiationException, IllegalAccessException { t = clazz.newInstance(); } } public class Dirver { public static void main(String[] args) throws InstantiationException, IllegalAccessException { Demo<String> demo = new Demo<>(String.class); String t = demo.t; System.out.println(t == null); // 输出 false } }
3、泛型数组实例化错误示例
class Demo<T> { T[] t = new T[10]; }
4、泛型数组实例化正确示例
import java.lang.reflect.Array; class Demo<T> { T[] t; Demo(Class<T> clzz, int length) { t = (T[]) Array.newInstance(clzz, length); } } public class Dirver { public static void main(String[] args) { Demo<String> demo = new Demo<>(String.class, 5); String[] t = demo.t; System.out.println(t.length); // 输出5 } }
5、泛型实例化的另外一个例子
import java.lang.reflect.Array; import java.util.ArrayList; public class Dirver { public static void main(String[] args) { // 报错 // ArrayList<String>[] listArr1 = new ArrayList<String>[5]; // 运行时报错 // ArrayList<String>[] listArr2 = new ArrayList<>[5]; // 可以 ArrayList<String>[] listArr3 = new ArrayList[5]; listArr3[0] = new ArrayList<String>(); listArr3[0].add("1"); // 可以 ArrayList<String>[] listArr4 = (ArrayList[])Array.newInstance(ArrayList.class, 5); listArr4[0] = new ArrayList<String>(); listArr4[0].add("2"); // 输出 1 2 System.out.println(listArr3[0].get(0) + '\t' + listArr4[0].get(0)); } }
六、泛型一个问题
1、一个方法的形参是 List<Number>,传入一个List<Integer>类型的对象报错。
import java.util.LinkedList; import java.util.List; public class Demo { public static void main(String[] args) { demo(new LinkedList<Number>()); // 以下报错 //demo(new LinkedList<Integer>()); } public static void demo(List<Number> list) {} }
注意 1:一个List<Number>对象的add方法可以传入一个Integer类型的元素。add方法要求传入一个Number,传入Integer会有自动转型。
public static void main(String[] args) { List<Number> list = new ArrayList(); list.add(new Float(1.0)); list.add(new Integer(100)); list.add(new Double(99.9)); list.stream().forEach( n -> System.out.println(n)); // 输出 // 1.0 // 100 // 99.9 Number number = list.get(0); }
注意 2:List<List<Number>>对象不可以传入一个new ArrayList<Integer>(),可以传入new ArrayList<Number>()。
import java.util.ArrayList; import java.util.List; class Demo { void demo() { List<List<Number>> list = new ArrayList<>(); list.add(new ArrayList<Number>()); // 下面报错 list.add(new ArrayList<Integer>()); } }
2、试图解决方案一
import java.util.LinkedList; import java.util.List; public class Demo { public static void main(String[] args) { // 以下两个都报错 demo(new LinkedList<Number>()); demo(new LinkedList<Integer>()); } public static void demo(List<Object> list) {} }
3、试图解决方案二
import java.util.List; public class Demo { public static void main(String[] args) { } // 方法重载失败,报错 public static void demo(List<Number> list) {} public static void demo(List<Integer> list) {} }
4、原因分析
import java.util.ArrayList; public class Demo { public static void main(String[] args) { ArrayList<Number> num1 = new ArrayList<>(); ArrayList<Integer> num2 = new ArrayList<>(); System.out.println(num1.getClass().getSimpleName()); // 输出ArrayList System.out.println(num2.getClass().getSimpleName()); // 输出ArrayList System.out.println(num1.getClass() == num2.getClass()); // 输出true } }
List<Number>和List<Integer>是同一种类型。
List<Number>和List<integer>泛型不同。
当方法形参是List<Number>,传入实参是List<Integer>时,泛型不同,所以报错。
5、解决办法一
import java.util.LinkedList; import java.util.List; public class Demo { public static void main(String[] args) { demo(new LinkedList<Number>()); demo(new LinkedList<Integer>()); } public static void demo(List<?> list) {} }
6、解决办法二
import java.util.LinkedList; import java.util.List; public class Demo { public static void main(String[] args) { demo(new LinkedList<Number>()); demo(new LinkedList<Integer>()); } public static void demo(List<? extends Number> list) {} }
六、泛型的另一个问题
1、声明为List<? extends E>类型的变量无法添加元素
import java.util.ArrayList; import java.util.List; public class Demo { public static void main(String[] args) { List<? extends Object> list = new ArrayList<>(); // 报错 list.add(new Object()); } }
2、声明为List<? super Nuber>类型的变量,可以添加Number类型及其子类类型的元素
import java.util.ArrayList; import java.util.List; public class Demo { public static void main(String[] args) { List<? super Number> list = new ArrayList<>(); list.add((Number)100); list.add((Integer)100); list.add(1.23); list.add(100F); list.add(new Byte("10")); Object object = list.get(0); // 报错 list.add(Object); } }
七、泛型擦除
1、
验证
import java.lang.reflect.Field; class Eraser<T> { public T t; } public class Dirver { public static void main(String[] args) throws NoSuchFieldException { Eraser<String> eraser = new Eraser<>(); Field field = eraser.getClass().getFields()[0]; String name = field.getName(); String typeName = field.getType().getSimpleName(); // 输出 t Object System.out.println(name + '\t' + typeName); } }
2、
验证:
import java.lang.reflect.Field; class Eraser<T extends Number> { public T t; } public class Dirver { public static void main(String[] args) throws NoSuchFieldException { Eraser<Integer> eraser = new Eraser<>(); Field field = eraser.getClass().getFields()[0]; String name = field.getName(); String typeName = field.getType().getSimpleName(); // 输出 t Number System.out.println(name + '\t' + typeName); } }
3、
4、
八、泛型一个应用
import java.util.Arrays; import java.util.Comparator; import java.util.TreeSet; /** * TreeSet(Comparator<? super E> comparator)的应用 */ class Person { String name; } class Student extends Person{ Integer studentNo; Student(String name, Integer studentNo) { this.name = name; this.studentNo = studentNo; } } public class Dirver { public static void main(String[] args) { Comparator<Person> comparatorByName = (a, b) -> a.name.compareTo(b.name); Comparator<Student> comparatorByNo = (a, b) -> a.studentNo - b.studentNo; TreeSet<Student> setOrderByName = new TreeSet<Student>(comparatorByName); TreeSet<Student> setOrderByNo = new TreeSet<Student>(comparatorByNo); Student[] students = {new Student("Tom", 100), new Student("Jim", 300), new Student("Lucy", 200)}; setOrderByName.addAll(Arrays.asList(students)); setOrderByNo.addAll(Arrays.asList(students)); setOrderByName.stream().forEach(student -> System.out.println(student.name + "-" +student.studentNo)); setOrderByNo.stream().forEach(student -> System.out.println(student.name + "-" +student.studentNo)); } }