泛型
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 + "]";
}
}

浙公网安备 33010602011771号