一、泛型的理解及其使用
1.1 什么是泛型?
- 所谓泛型,就是允许在定义类、接口时通过一个`标识'`表示类中某个`属性的类型"或者是某个方法的 "返回值或参数的类型"。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时) 确定(即传入实际的类型参数,也称为类型实参)。
1.2 不使用泛型的集合可能存在的问题
- 问题1:类型不安全。因为add()的参数是0bject类型,意味着任何类型的对象都可以添加成功
- 问题2:需要使用强转操作,繁琐。还有可能导致ClassCastException异常。
1.3 泛型在集合、比较器中的使用
java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CollectionTest {
//在集合中使用泛型
@Test
public void test(){
List list = new ArrayList();
//只添加整数类型的
list.add(54);
list.add(24);
list.add(34);
list.add(64);
list.add(88);
// 问题1 :类型不匹配,只希望添加整数类型,但是其他类型的也可以添加
// list.add("CC");
// 在遍历的时候需要进行强转,可能出现ClassCastException
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Integer i = (Integer) iterator.next();
int score = i;
System.out.println(score);
}
}
@Test
public void test2(){
List<Integer> list = new ArrayList();
list.add(54);
list.add(24);
list.add(34);
list.add(64);
list.add(88);
Iterator<Integer> iterator = list.iterator();
//不需要使用强转,因为使用的就是Integer类型
while(iterator.hasNext()){
Integer i = iterator.next();
int score = i;
System.out.println(score);
}
}
}
1.4 泛型在Map中的使用
java
@Test
public void test3(){
HashMap<String ,Integer> map = new HashMap<>();//类型推断
map.put("Alice",56);
map.put("Jerry",26);
map.put("Emma",37);
map.put("Rose",45);
//原本的写法
/* Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator();*/
//使用var
var entrySet = map.entrySet();
var iterator = entrySet.iterator();
while(iterator.hasNext()){
Map.Entry<String, Integer> entry = iterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + "--->" +value);
}
}
1.5 练习
java
package fanxingTest;
public class Employee implements Comparable<Employee> {
private String name;
private int ager;
private MyDate birthday;
public Employee() {
}
public Employee(String name, int ager, MyDate birthday) {
this.name = name;
this.ager = ager;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAger() {
return ager;
}
public void setAger(int ager) {
this.ager = ager;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "员工{" +
"name='" + name + '\'' +
", ager=" + ager +
", birthday[" + birthday +
"]}";
}
@Override
public int compareTo(Employee o) {
return this.name.compareTo(o.name);
}
}
java
package fanxingTest;
public class MyDate implements Comparable<MyDate>{
private int year;
private int month;
private int day;
public MyDate() {
}
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 String toString() {
return year +"年" + month +"月" + day +"日";
}
@Override
public int compareTo(MyDate o) {
//年月日一个一个的去比
int yearDistance = this.getYear() - o.getYear();
if (yearDistance != 0) {
return yearDistance;
}
int monthDistance = this.getMonth() - o.getMonth();
if (monthDistance != 0) {
return monthDistance;
}
return this.getDay() - o.getDay();
}
}
java
package fanxingTest;
import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.TreeSet;
public class EmployeeTest {
//自然排序
@Test
public void test(){
TreeSet<Employee> set = new TreeSet<>();
Employee e1 = new Employee("zhansan",24,new MyDate(2000,12,23));
Employee e2 = new Employee("wanwu",21,new MyDate(2003,12,3));
Employee e3 = new Employee("alibba",26,new MyDate(1998,3,23));
Employee e4 = new Employee("guanyu",35,new MyDate(1989,5,6));
Employee e5 = new Employee("yuweiwen",41,new MyDate(1983,6,21));
set.add(e1);
set.add(e2);
set.add(e3);
set.add(e4);
set.add(e5);
Iterator<Employee> iterator = set.iterator();
while(iterator.hasNext()){
Employee employee = iterator.next();
System.out.println(employee);
}
}
//定制排序
@Test
public void test2(){
Comparator<Employee> comparator = new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
/* 年月日一个一个的去比,也可以放到类的中去调用
int yearDistance = o1.getBirthday().getYear() - o2.getBirthday().getYear();
if (yearDistance != 0) {
return yearDistance;
}
int monthDistance = o1.getBirthday().getMonth() - o2.getBirthday().getMonth();
if (monthDistance != 0) {
return monthDistance;
}
return o1.getBirthday().getDay() - o2.getBirthday().getDay();
*/
return o1.getBirthday().compareTo(o2.getBirthday());
}
};
TreeSet<Employee> set1 = new TreeSet<>(comparator);
Employee e1 = new Employee("zhansan",24,new MyDate(2003,12,23));
Employee e2 = new Employee("wanwu",21,new MyDate(2003,12,3));
Employee e3 = new Employee("alibba",26,new MyDate(1998,3,23));
Employee e4 = new Employee("guanyu",35,new MyDate(1989,5,6));
Employee e5 = new Employee("yuweiwen",41,new MyDate(1983,6,21));
set1.add(e1);
set1.add(e2);
set1.add(e3);
set1.add(e4);
set1.add(e5);
Iterator<Employee> iterator = set1.iterator();
while(iterator.hasNext()){
Employee employee = iterator.next();
System.out.println(employee);
}
}
}
二、自定义泛型类,泛型方法
2.1 自定义泛型接口\类
格式:
class A<>{
}
class B<T1,T2>{
}
2.1.1 使用说明
- 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型,
- 我们在创建自定义泛型类的对象时,可以指明泛型参数类型,一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
- 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类均按照0bject处理,但不等价于0bject
经验:泛型要使用一路都用。要不用,一路都不要用。
- 泛型的指定中必须使用引用数据类型。不能使用基本数据类,此时只能使用包装类替换。
- 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数
如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。
我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。
2.1.2 注意
- 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
- JDK7.0 开始,泛型的简化操作:ArrayList<Fruit>flist = new ArrayList<>();
- 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
- 不能使用new E[]。但是可以:E[]elements=(E[])new 0bject[capacity];
- 参考:ArrayList源码中声明:0bject[]elementData,而非泛型参数类型数组。
- 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
- 异常类不能是带泛型的。
2.2 自定义泛型方法
格式:
public <E> E methon(E e){//<E>的作用告诉编译器,这个方法是泛型方法
}
2.3 泛型的应用练习
java
package exer1;
import java.util.*;
public class DAO<T> {
Map<String,T> map = new HashMap<>();
public void save(String id,T entity){//: 保存 T 类型的对象到 Map 成员变量中
if (!map.containsKey(id)) {
map.put(id, entity);
}
}
public T get(String id){//:从 map 中获取 id 对应的对象
return map.get(id);
}
public void update(String id,T entity){//:替换 map 中key为id的内容,改为 entity 对象
if (map.containsKey(id)) {
map.put(id, entity);
}
}
public List<T> list(){//返回 map 中存放的所有 T 对象
Collection<T> values = map.values();
ArrayList<T> list = new ArrayList<>(values);
return list;
}
public void delete(String id){//:删除指定 id 对象
map.remove(id);
}
}
java
package exer1;
import java.util.Objects;
public class User {
private int id;
private int age;
private String name;
public User() {
}
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id == user.id && age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(id, age, name);
}
}
java
package exer1;
import org.junit.Test;
import java.util.List;
public class UserTest {
@Test
public void test(){
DAO<User> dao = new DAO<>();
dao.save("1001",new User(1,32,"林俊杰"));
dao.save("1002",new User(2,41,"罗志祥"));
dao.update("1002",new User(3,35,"于文文"));
dao.delete("1002");
List<User> lIst = dao.list();
for (User u : lIst){
System.out.println(u);
}
}
}
三、泛型在继承上的体现及通配符的使用
2.1 泛型在继承上的体现
1.类SuperA是类A的父类,则G<SuperA>与G<A>的关系
- G<SuperA>和 G<A>是并列的两个类,没有任何子父类的关系
- 比如:ArrayList<0bject>、ArrayList<String>没有
2.类SuperA是类A的父类或接口,Super<G>与A<G>的关系
- SuperA<G>与A<G>有继承或实现的关系
- 即A<G>的实例可以赋值给SuperA<G>类型的引用(或变量)
- 比如:List<String>与 ArrayList<String>
3.2 通配符的使用
1.通配符:?
2.使用说明:
- 举例:ArrayList<?>
- G<?> 可以看做是G<A>类型的父类,即可以将G<A>的对象赋值给G<?>类型的引用(或变量)
3.读写数据的特点(以集合ArrayList<?>为例说明)
- 读取数据:允许的,读取的值的类型为0bject类型
- 写入数据:不允许的。特例:写入null值。
4.有限制条件的通配符
- List<? extends A>
- List <? super A> :