1、
/* * 在JDK1.5之前(泛型之前之前),我们通过集合存取元素会具有 * 两个缺陷: * 1 繁琐性 * 2 不安全性 */ package day13; import java.util.ArrayList; import java.util.List; /* * 非泛型的繁琐性 */ public class NoGeneric { public static void main(String[] args) { List list = new ArrayList(); // 通过调用add方法向集合类中加入元素。 list.add("aaa"); list.add("bbb"); list.add("ccc"); // 通过get方法获取元素,参数指定索引值。(从0开始) // 我们往往只操作同一类型的元素,虽然我们明知道加入 // 的就是String类型,但是,获取元素时,也只能获取 // Object类型,我们还需要显示的执行类型转换。(非 // 泛型的繁琐性。) String s = (String) list.get(1); } }
package day13; import java.util.ArrayList; import java.util.List; public class NoGeneric2 { /* * 非泛型的不安全性 */ public static void main(String[] args) { List list = new ArrayList(); list.add("one"); list.add("two"); list.add("three"); // 我们原意只是用集合来存储String类型的元素, // 但是由于程序员的疏忽,我们加入了一个非String // 类型的元素,编译器也不会产生编译错误。 list.add(new Integer(1)); // 我们获取元素时,还以为元素都是String类型。 // 编译时没有错误,但在运行时会产生 // ClassCastException异常。(非泛型的不安全性) String s = (String) list.get(3); } }
2、泛型设计
/* * 泛型设计 * 泛型就是在类型(类,接口)或方法(构造器)后加上一个<类型参数>。 * 一个类型加上具体的类型参数,我们称之为参数化类型。 * 泛型就是含有一个类型参数(泛型声明时,指定的类型参数 * 称为形式类型参数),在使用类型时,传递一个具体的类型 * (使用泛型时,传递的参数称为实际类型参数)。这类似于方法 * 调用过程中的参数传递,只是方法参数传递,传递的是一个具体 * 的值,而泛型中参数传递,传递的是一个具体的类型。 * * 按照惯例,泛型声明处的类型参数使用一个大写字母表示。 * E 元素element * T 类型Type * K 键key * V 值value * * 泛型的类型参数可以是任意的引用类型。(不能是基本数据类型。) */ package day13; import java.util.ArrayList; import java.util.List; public class Generic { public static void main(String[] args) { // 使用泛型,表示当前集合仅能存储实际类型参数指定的类型。 List<String> list = new ArrayList<String>(); list.add("aaa"); list.add("bbb"); // 无需再进行类型转换,消除了繁琐性。 String s = list.get(0); // 错误,编译器会进行严格的类型检查,消除了不安全性。 // list.add(new Integer(5)); List<Integer> list2 = new ArrayList<Integer>(); list2.add(new Integer(5)); list2.add(5); // list2.add(Integer.valueOf(5)); int x = list2.get(0).intValue(); x = list2.get(0); // 错误的。 // List<int> list3; // 可以是数组类型,因为数组类型也是引用类型。 List<int[]> list3; } }
3、原生类型
/* * 在使用泛型类时,没有给出具体的实际类型参数,这种类型 * 称为原生类型。例如: * List list = new ArrayList(); * List与ArrayList就是原生类型。 * 原生类型仅仅作为历史遗留的产物,之所以还能使用,那是因为 * 是对以前程序的兼容与让步。我们在以后使用泛型类时,不要在 * 使用原生类型,应该总是使用参数化类型来代替原生类型。 */ package day13; public class RawType { public static void main(String[] args) { } }
4、类型推断
/* * 类型推断 */ package day13; import java.util.ArrayList; import java.util.List; public class Inference { public static void main(String[] args) { List<String> list = new ArrayList<String>(); // 从JDK1.7起,编译器可以自动进行类型推断 // 从不规范的角度说,把这种类型称为"菱形语法"。 List<String> list2 = new ArrayList<>(); List<List<String>> list3; } }
5、参数化类型的继承
/* * 参数化类型的“继承” * 存在一种类型,就会存在对应的数组类型。 * 如果A是B的子类型,则A[]也是B[]子类型。 * * 参数化类型不具有可继承性。 * 如果A是B的子类型,则T<A>不是T<B>的子类型。 * T是某泛型类型。 */ package day13; import java.util.ArrayList; import java.util.List; public class GenericInherit { public static void main(String[] args) { // String是Object的子类型,因此,String[] // 也是Object[]的子类型。 String[] s = new String[5]; Object[] o = s; // o[0] = new Integer(5); // 错误,在编译时没有问题,但是,在运行时会产生 // ArrayStoreException异常。 o[0] = 5; List<String> list = new ArrayList<>(); List<Object> list2 = new ArrayList<>(); // 错误,参数化类型不具有可继承性 // list2 = list; // list2.add(new Object()); // list2.add(new Integer(5)); print(list2); // print(list); } public static void print(List<Object> list) { // 对任意的List进行操作 } }
6、类型通配符?
/* * 类型通配符? * ?表示某种类型,是一种不确定的类型。 * 含有通配符的参数化类型就是含有具体类型参数的参数化 * 类型的父类型。 * * 类型通配符有三种:(X表示某种类型,也可以是接口类型) * ? 无界通配符。可以是任意的引用类型。 * ? extends X 含有上界的通配符,可以是X类型或X类型的子类型。 * ? super X 含有下界的通配符,可以是X类型或X类型的父类型。 * */ package day13; import java.util.ArrayList; import java.util.List; public class Wildcard { public static void main(String[] args) { List<String> list = new ArrayList<>(); List<Integer> list2 = new ArrayList<>(); List<Object> list3 = new ArrayList<>(); print(list); print(list2); print(list3); } // 通过类型通配符,就可以接收类型参数是任意类型的参数化类型。 public static void print(List<?> list) { // 当使用类型通配符时,向集合中加入元素会受到一定的限制。 // 但是可以加入null值,因为null可以赋值给任意的引用 // 类型。 // list.add(""); // list.add(5); // list.add(new Object()); // 可以加入null。 list.add(null); } }
package day13; import java.util.ArrayList; import java.util.List; public class Wildcard2 { public static void main(String[] args) { List<Rectangle> list = new ArrayList<>(); List<Shape> list2 = new ArrayList<>(); eva(list); eva(list2); } public static void eva(List<? extends Shape> list) { // 循环取出每一个图形,然后计算周长与面积 Shape s = list.get(0); System.out.println(s.per()); System.out.println(s.area()); // list.add(new Rectangle()); } } abstract class Shape { public abstract double per(); public abstract double area(); } class Rectangle extends Shape { @Override public double per() { return 1; } @Override public double area() { return 1; } }
package day13; import java.util.ArrayList; import java.util.List; public class Wildcard3 { public static void main(String[] args) { List<Rectangle> list = new ArrayList<>(); List<Shape> list2 = new ArrayList<>(); List<Object> list3 = new ArrayList<>(); Rectangle r = new Rectangle(); add(list, r); add(list2, r); add(list3, r); } public static void add(List<? super Rectangle> list, Rectangle s) { // 加入的操作 list.add(null); list.add(s); } }
7、自定义泛型类
/* * 自定义泛型类 * * 在声明类后,再声明一个(或多个)形式类型参数, * 这样的类就是泛型类。多个形式类型参数使用","进行分隔。 * 按照惯例,形式类型参数使用一个大写字母表示。 * * 在使用泛型类(参数化类型)时,泛型类的类体可以看成是 * 实际类型参数替换掉形式类型参数后的结果。(但实际上, * 泛型类只有一个,不会因为实际类型参数的不同而不同。) */ package day13; public class GenericClass { public static void main(String[] args) { Box<String> box = new Box<>(); Box<Integer> box2 = new Box<>(); box.setT("abc"); // box.setT(5); String s = box.getT(); box2.setT(10); //box2.setT("23"); int x = box2.getT(); } } class Box<T> { private T t; public void setT(T t) { this.t = t; } public T getT() { return t; } } /* class Box<String> { private String t; public void setT(String t) { this.t = t; } public String getT() { return t; } } class Box<Integer> { private Integer t; public void setT(Integer t) { this.t = t; } public Integer getT() { return t; } } */
8、
/* * 泛型类的类型参数(形式类型参数)也可以指定界限。 * * 类型参数与通配符: * 1 类型参数只能用extends指定上界,不能使用super指定下界。 * (通配符既可以指定上界,也可以指定下界) * 2 类型参数可以作为一种类型而存在,但是通配符不能。 * 3 类型参数可以指定一个以上(多个)上界,但是通配符不行。 * * 多个上界: * 1 多个上界既可以是类类型,也可以是接口类型。 * 2 如果两个(多个)上界同时是接口类型,顺序没有要求。 * 3 如果两个(多个)上界有一个是类类型,则类必须放在前面。 */ package day13; public class GenericClass2 { public static void main(String[] args) { Box2<Shape> box; Box2<Rectangle> box2; // Box2<String> box3; // Box3<Shape> box4; // Box3<Rectangle> box3; Box3<Circle> box4; } } class Box2<T extends Shape> { // 在类中使用类型参数所代表的类型。 T t; // 通配符不能作为一种类型。 // ? t; //List<?>与List<? extends Object> 等价 } interface Inter { } // 类型参数指定多个上界 class Box3<T extends Shape & Inter> { } //错误,对于多个上界,类必须声明在接口前。 //class Box3<T extends Inter & Shape> { } class Circle extends Shape implements Inter { @Override public double per() { return 0; } @Override public double area() { return 0; } }
9、泛型方法
/* * 泛型方法。 * 方法也可以声明为泛型方法。就是在方法的返回类型前 * 声明一个或多个类型参数。泛型方法声明的类型参数在 * 方法中是有效的。 * * 对泛型方法调用时,可以在.后面显示指定实际类型参数。 * gm.<String>g("abc"); * 但是,通常情况下,我们不需要这样做,编译器可以自动 * 推断泛型方法的类型参数。 * gm.g("abc"); * 如果需要显示指定类型参数,则引用不能丢失。 */ package day13; import java.util.List; public class GenericMethod { public static void main(String[] args) { GenericMethod gm = new GenericMethod(); // 泛型方法的调用 // gm.<String>g("abc"); // List<Integer> list = null; // gm.g("abc", list); gm.g("a", 10); } public <T> void g(T t, T t2) { // 方法体 //如果需要显示指定类型参数,则引用不能丢失。 //<Integer>k(20); 错误 this.<Integer>k(20); } public <E> void k(E t) { } } /* * public class GenericMethod<T> { public static void main(String[] args) { * * } * * public void g(T t) { List<T> list; } * * public void k(T t) { * * } } */
10、泛型构造器
/* * 泛型构造器 * 可以声明泛型构造器,就是在声明构造器名的前面 * 指定一个或多个类型参数。 */ package day13; public class GenericCon { public static void main(String[] args) { // 显示指定构造器的实际类型参数。 GenericCon c = new <String>GenericCon("abc"); // 编译器也可以自动推断构造器的实际类型参数。 GenericCon c2 = new GenericCon("abc"); GenericC<String> gc = new GenericC<>("abc"); //当构造器是泛型构造器,类是泛型类,当我们显式的为构造器 //指定类型参数,则此时,菱形语法将不能使用。 // 错误 // GenericC<String> gc2 = new <Integer>GenericC<>(10); GenericC<String> gc2 = new <Integer>GenericC<String>(10); } public <T> GenericCon(T t) { } } class GenericC<T> { public <E> GenericC(E e) { } }
11、泛型擦除
/* * 泛型的擦除 * 泛型可以提供编译期间的类型检查。然而,这种类型检查也仅仅 * 只能发生在编译期间,在编译之后生成的字节码文件(.class文件) * 中,所有的泛型信息都将会被擦除(所有的泛型信息都将丢失)。 * * 擦除前与擦除后表示: * 1 对于参数化类型,会使用原生类型进行替换。 * 2 对于类型参数,会使用类型参数的上界进行替换。 * 1) 对于无界的类型参数,使用Object进行替换。 * 2) 对于含有一个上界的类型参数,使用上界进行替换。 * 3) 对于含有多个上界的类型参数,使用第一个上界进行替换。 * * 因为在编译过后,再没有参数化类型,因此,instanceof右侧 * 的类型就不能是参数化类型。 * obj instanceof List<String> 错误 */ package day13; public class Eraser { public static void main(String[] args) { // List<String> list = new ArrayList<>(); // 擦除后 // List list = new ArrayList(); } // public <T> void f(T t) {} // 擦除后: // public void f(Object o) {} // public <T extends Shape> void g(T t) {} // 擦除后 // public void g(Shape t) { } // public <T extensd Type1 & Type2> void k(T t) {} // 擦除后 // public void k(Type1 t) {} }
12、泛型方法的重载
/* * 泛型方法的重载 * 当泛型方法参与重载时,能够进行重载,要看泛型 * 方法擦除后的参数列表而定。 */ package day13; public class GenericOverload { /* * 不能重载 * public void f(List list) { } * public void f(List<String> list) { } * 可以重载 * public void f(String t) { } * public <T> void f(T t) { } * 不能重载 * public void f(Object o) { } * public <T> void f(T t) { } * 不能重载 * public <E> void f(E o) { } * public <T> void f(T t) { } * 可以重载 * public <E extends Shape> void f(E o) { } * public <T> void f(T t) { } * 不能重载 * public <E extends Type1 & Type2> void f(E o) { } * public <T extends Type1> void f(T t) { } * 可以重载 * public <E extends Type2 & Type1> void f(E o) { } * public <T extends Type1> void f(T t) { } * 不能重载 * public void f(int[] x) { } * public void f(int... x) { } */ }
13、泛型方法的重写
/* * 泛型方法的重写 * 方法重写的第2点规则: * 子类重写父类的方法,则要求子类与父类的参数列表类型一致, * 或者与父类参数列表擦除后的类型一致。 */ package day13; import java.util.List; public class GenericOverride { public static void main(String[] args) { } } class Super { public void f(List<String> list) { } public void g(List list) { } } class Sub extends Super { @Override public void f(List list) { } // @Override // public void g(List<String> list) {} }
14、对象的比较 Comparable Comparator
package day13; import java.util.Arrays; import java.util.Comparator; public class SortTest { public static void main(String[] args) { int[] x = { 5, 3, 1, 100, -2, 30 }; Arrays.sort(x); System.out.println(Arrays.toString(x)); Integer[] x2 = { 5, 3, 1, 100, -2, 30 }; Arrays.sort(x2); System.out.println(Arrays.toString(x2)); Student[] stu = { new Student(1, "a"), new Student(3, "b"), new Student(20, "c"), new Student(2, "d") }; Arrays.sort(stu); // System.out.println(Arrays.toString(stu)); // 自行指定比较规则 // Arrays.sort(stu, new MyOrder()); // System.out.println(Arrays.toString(stu)); // Arrays.sort(stu, new Comparator<Student>() { // @Override // public int compare(Student o1, Student o2) { // return o2.getNo() - o1.getNo(); // } // }); // System.out.println(Arrays.toString(stu)); Arrays.sort(stu, (o1, o2) -> o2.getNo() - o1.getNo()); System.out.println(Arrays.toString(stu)); } } class MyOrder implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { // return o1.getNo() - o2.getNo(); return o2.getNo() - o1.getNo(); } } class Student implements Comparable<Student> { private int no; private String name; public int getNo() { return no; } public void setNo(int no) { this.no = no; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student(int no, String name) { super(); this.no = no; this.name = name; } @Override public int compareTo(Student o) { // if (no > o.no) { // return 1; // } else if (no == o.no) { // return 0; // } else { // return -1; // } return no - o.no; } @Override public String toString() { return "Student [no=" + no + ", name=" + name + "]"; } }