Java--泛型

一、泛型的理解及其使用

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 注意

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
  2. JDK7.0 开始,泛型的简化操作:ArrayList<Fruit>flist = new ArrayList<>();
  3. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
  4. 不能使用new E[]。但是可以:E[]elements=(E[])new 0bject[capacity];
  5. 参考:ArrayList源码中声明:0bject[]elementData,而非泛型参数类型数组。
  6. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
  7. 异常类不能是带泛型的。

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> :
相关推荐
景天科技苑3 分钟前
【Golang】Go语言中如何进行包管理
开发语言·后端·golang·go mod·go语言包管理·go包管理·go sum
AIGC绘画4 分钟前
Spring微服务概述之spring cloud alibaba服务调用实践
java·spring·微服务
wwangxu5 分钟前
Java 面向对象基础
java·开发语言
wdxylb21 分钟前
Linux下编写第一个bash脚本
开发语言·chrome·bash
uzong22 分钟前
JDK高性能套路: 自旋(for(;;)) + CAS
java·后端
幽兰的天空23 分钟前
Python实现的简单时钟
开发语言·python
这题怎么做?!?31 分钟前
模板方法模式
开发语言·c++·算法
幽兰的天空1 小时前
简单的Python爬虫实例
开发语言·爬虫·python
带刺的坐椅1 小时前
Spring SPI、Solon SPI 有点儿像(Maven 与 Gradle)
java·spring·solon·spi
冷眼看人间恩怨1 小时前
【Java】揭秘网络编程:深入探索其无尽奥秘与魅力
java·开发语言·tcp/ip·udp·tcp