泛型 Generic
泛型的理解和好处
使用传统方法的问题
- 不能对加入到集合
ArrayList
中的数据类型进行约束(不安全)
- 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
用泛型来解决
1
| ArrayList<Dog> arrayList = new ArrayList<Dog>();
|
- 如果编译器发现添加的类型不满足要求,就会报错
- 在遍历的时候,可以直接取出Dog类型而不是Object,
不用进行向下转型
public class ArrayList<E> {} E称为泛型, 那么 Dog
\(\rightarrow\) E
泛型说明
int a = 10; 泛(广泛)型(类型) E => Integer, String, Dog
- 泛型又称参数化类型,是 Jdk5.0 出现的新特性, 解决数据类型的安全性问题
【泛型可以理解为接收数据类型的数据类型】
- 在类声明或实例化时只要指定好需要的具体的类型即可。
- Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生
ClassCastException 异常。同时,代码更加简洁、健壮
- 泛型的作用是:
可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
1 2 3 4 5 6 7 8 9 10 11 12 13
| Person<String> p = new Person<String>("hello");
class Person<E> { E s; public Person(E s) { this.s = s; } public E f() { return s; } }
|
泛型语法
泛型的声明
interface接口<T>和class类<K,V>{} //比如: List, ArrayList
说明:
- 其中,T,K,V不代表值,而是表示类型。
- 任意字母都可以。常用T表示,是Type的缩写
泛型的实例化 要在类名后面指定类型参数的值(类型)。如:
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public class Generic01 { public static void main(String[] args) { HashSet<Stu> stus = new HashSet<>(); Stu s1 = new Stu("jojo", 18); Stu s2 = new Stu("kiki", 5); Stu s3 = new Stu("lala", 8); stus.add(s1); stus.add(s2); stus.add(s3);
for (Stu stu : stus) { System.out.println(stu); } System.out.println("+++++++++++++++++++"); Iterator<Stu> iterator = stus.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }
HashMap<String, Stu> map = new HashMap<>(); map.put("jojo", s1); map.put("kiki", s2); map.put("lala", s3); System.out.println("+++++++++++++++++++"); for (String s : map.keySet()) { System.out.println(s + " - " + map.get(s)); } System.out.println("+++++++++++++++++++"); Iterator<Map.Entry<String, Stu>> entryIterator = map.entrySet().iterator(); while (entryIterator.hasNext()) { Map.Entry<String, Stu> next = entryIterator.next(); System.out.println(next.getKey() + " - " + next.getValue());
} } }
class Stu { String name; int age;
public Stu(String name, int age) { this.name = name; this.age = age; }
@Override public String toString() { return "Stu{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
泛型使用细节
interface List<T>{}, public class HashSet<E>{}
... 等等 说明: T, E 只能是引用类型 看看下面语句是否正确?
1 2
| List<Integer> list = new ArrayList<Integer>(); List<int> list2 = new ArrayList<int>();
|
- 在指定泛型具体类型后, 可以传入该类型或者其子类类型
1 2 3 4 5 6 7 8 9 10 11 12
| Person<A> p1 = new Person<>(new A()); Person<A> p2 = new Person<>(new B());
class A{} class B extends A {} class Person<E> { E e; public Person(E e) { this.e = e; } }
|
- 泛型使用形式
1 2
| List<Integer> list1 =new ArrayList<Integer>(); List<Integer> list2 = new ArrayList<>();
|
[说明] 如果这样写 List list3 = new ArrayList();
默认给它的泛型是[<E> E就是Object]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| List<MyDate> as = new ArrayList<>(); as.sort(new Comparator<MyDate>() { @Override public int compare(MyDate o1, MyDate o2) { return o1.getBirthday().compareTo(o2.getBirthday()); } });
class MyDate implements Comparable<MyDate> { private int year; private int month; private int day;
public MyDate(int year, int month, int day) { this.year = year; this.month = month; this.day = day; }
public int getYear() { return year; }
public void setYear(int year) { this.year = year; }
public int getMonth() { return month; }
public void setMonth(int month) { this.month = month; }
public int getDay() { return day; }
public void setDay(int day) { this.day = day; }
@Override public int compareTo(MyDate o) { int yearMinus = year - o.getYear(); if (yearMinus != 0) return yearMinus; int monthMinus = month - o.getMonth(); if (monthMinus != 0) return monthMinus; return day - o.getDay(); }
@Override public String toString() { return "MyDate{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; } }
|
自定义泛型
泛型类
1 2 3
| class 类名<T, R, M...> { 成员 }
|
- 普通成员(属性、方法)可以使用泛型
- 使用泛型的数组, 不能初始化
T[] ts = new T[10];
没有确定类型不知道在内存开辟多大空间
- 静态方法、静态属性中不能使用类的泛型,因为静态是和类相关的,在类加载时,对象还没创建,无法确定类型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时, 没有指定类型, 默认为Object
泛型接口
1 2 3
| interface 接口名<T, R…> {
}
|
- 接口中, 静态成员也不能使用泛型(这个和泛型类规定一样)
- 泛型接口的类型,
在继承接口或者实现接口时确定
- 没有指定类型, 默认为Object
泛型方法
1 2 3
| 修饰符 <T,R..> 返回类型 方法名(参数列表) {
}
|
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会确定
public void eat(E e) {}, 修饰符后没有
<T,R...> , eat方法不是泛型方法, 而是使用了泛型
泛型继承和通配符
- 泛型不具备继承性
List<Object> list = new ArrayList<String>();
x
<?> : 支持任意泛型类型
<? extends A>: 支持A类以及A类的子类,
规定了泛型的上限
<? super A>: 支持A类以及A类的父类,
不限于直接父类,规定了泛型的下限
JUnit
- 一个类有很多功能代码需要测试,为了测试,就需要写入到main方法中
- 如果有多个功能代码测试,就需要来回注销,切换很麻烦
- JUnit 可以直接运行一个方法,并且可以给出相关信息, 方便很多
- JUnit 是一个Java语言的单元测试框架
- 多数Java的开发环境都已经集成了 JUnit 作为单元测试的工具
1 2 3 4 5 6
| import org.junit.jupiter.api.Test;
@Test public void f() { }
|