Java学习之路(四十六)| 泛型

各自努力,最高处见!加油!

  • 编译时,约束加入集合中的元素类型,提高了安全性。如果编译器发现添加的类型不满足要求,就会报错。

  • 减少了类型的转换次数。在遍历的时候,可以直接取出该类型,而不是先取出Object对象再向下转型。

一、泛型的介绍

  1. 泛型又称参数化类型,是JDK5.0出现的新特性,解决数据类型的安全问题。
  2. 在类声明或实例化时只要指定好需要的具体类型即可。
  3. Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastExecption异常。同时,代码更加健壮、简洁。
  4. 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值类型,或者是参数类型。
//E 表示s的数据类型,该数据类型在定义Person对象时再指定,即在编译期间就知道E是什么类型。
class Person<E>{
		public Person(E s){
			this.s=s;
		}
		public E f(){
			return s;
		}
}

二、泛型的语法

声明

interface 接口<T>{}
class<K,V>{}
  1. K,T,V不代表值,而表示类型。
  2. 任意字母都可以,常用T表示,是Type的缩写。

泛型的实例化:

1.List<String> strList = new ArrayList<String>();
2.Iterator<Customer> iterator=customers.iterator();

泛型使用的注意事项和细节

  1. interface List< T >{},public class HashSet< E >{}
    说明:T,E只能是引用类型。
List< Integer >  list=new ArrayList< Integer >;
List< int >  list1=new ArrayList< int >;//错误
  1. 在指定泛型的具体类型后,可以传入该类型或其子类类型
  2. 泛型的使用形式
ArrayList<Integer> list1=new ArrayList<Integer>();
List<Integer> list2=new ArrayList<Integer>();
//在实际开发中,往往采用简写(右边不用写泛型)。编译器会进行类型推断,推荐用简写。
ArrayList<Integer> list1=new ArrayList<>();
List<Integer> list2=new ArrayList<>();

//未学习泛型的写法:默认给它的泛型是Object
ArrayList list1=new ArrayList();
List list2=new ArrayList();

示例代码

import java.util.ArrayList;
import java.util.Comparator;

public class Homework1 {
    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<>();
        Employee jack = new Employee("jack", 1000, new MyDate(2021, 10, 1));
        Employee tom = new Employee("tom", 2000, new MyDate(2021, 10, 2));
        Employee smith = new Employee("smith", 3000, new MyDate(2021, 10, 3));
        Employee smith2 = new Employee("smith", 3000, new MyDate(2021, 10, 4));

        employees.add(jack);
        employees.add(tom);
        employees.add(smith);
        employees.add(smith2);


        employees.sort(new Comparator<Employee>() {
            @Override
//            public int compare(Employee employee, Employee t1) {//自己写的
//                if (employee.getName()==t1.getName()){
//                    if(employee.getBirthday().getYear()==t1.getBirthday().getYear()){
//                        if (employee.getBirthday().getMonth()==t1.getBirthday().getMonth()){
//                            if (employee.getBirthday().getDay()==t1.getBirthday().getDay()){
//                                return 0;
//                            }
//                        }else {
//                            return -1;
//                        }
//                    }else {
//                        return -1;
//                    }
//                }else {
//                    return employee.getName().compareTo(t1.getName());//判断字符串字母的顺序
//                }
//                return 0;
//            }
            public int compare(Employee employee, Employee t1) {//韩顺平老师的示例代码:不用很多层if-else语句,可读性好
                int i=employee.getName().compareTo(t1.getName());
                if (i!=0){
                    return i;
                }
                //如果name相同,出生时间:这段代码最好让MyDate实现
//                int yearMinus=employee.getBirthday().getYear()-t1.getBirthday().getYear();
//                if (yearMinus!=0){
//                    return yearMinus;
//                }
//
//                int monthMinus=employee.getBirthday().getMonth()-t1.getBirthday().getMonth();
//                if (monthMinus!=0){
//                    return monthMinus;
//                }
//
//                int dayMinus=employee.getBirthday().getDay()-t1.getBirthday().getDay();
//                return dayMinus;
                return employee.getBirthday().compareTo(t1.getBirthday());//在MyDate中实现Comparator比较

            }
        });

        System.out.println(employees);
    }
}

class Employee{
    private String name;
    private int money;
    private MyDate birthday;

    public Employee(String name, int money, MyDate birthday) {
        this.name = name;
        this.money = money;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", money=" + money +
                ", birthday=" + birthday +
                '}';
    }
}

class MyDate implements Comparable<MyDate>{
    private int month;
    private int day;
    private int year;

    public MyDate(int year,int month, int day) {
        this.month = month;
        this.day = day;
        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;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "month=" + month +
                ", day=" + day +
                ", year=" + year +
                '}';
    }

//    MyDate实现时间的比较

    @Override
    public int compareTo(MyDate myDate) {
        int yearMinus=year-myDate.getYear();
        if (yearMinus!=0){
            return yearMinus;
        }

        int monthMinus=month-myDate.getMonth();
        if (monthMinus!=0){
            return monthMinus;
        }

        int dayMinus=day-myDate.getDay();
        return dayMinus;
    }
}

三、自定义泛型类

基本语法

class 类名<T,R...>{
	成员
}
  • 普通成员可以使用泛型(属性、方法)
  • 使用泛型的数组,不能初始化
  • 静态方法中不能使用类的泛型
  • 泛型类的类型,是在创建对象是确定的(因为创建对象时,需要指定确定类型)
  • 如果在创建对象时,没有指定类型,默认为Object

示例代码:

public class CustomGeneric_ {
    public static void main(String[] args) {
        Tiger tiger=new Tiger("John");//没有指定泛型的类型,全部默认为Object
    }
}

class Tiger<T,R,M>{
    String name;
    T t;//属性使用泛型
    M m;
    R r;
//    T[] ts=new T[8];//错误写法,泛型数组不能实例化。数组在new的时候不确定T的类型,不知道开辟多大的内存空间


    public Tiger(String name) {
        this.name = name;
    }
    public Tiger(String name, T t, M m, R r) {//构造器使用泛型
        this.name = name;
        this.t = t;
        this.m = m;
        this.r = r;
    }

    //静态方法中不能使用类的泛型。因为静态和类相关,在类加载的时,对象还没创建,
    //所以,如果静态方法和静态属性使用了泛型,JVM无法完成初始化。
    //简单来说:泛型的确定在构建对象的时候,但是静态的属性和方法在类编译时就要生成,此时编译器不知道类型是什么,所以无法创建变量。
//    public static void m1(M m){//错误写法
//
//    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    public M getM() {
        return m;
    }

    public void setM(M m) {
        this.m = m;
    }

    public R getR() {
        return r;
    }

    public void setR(R r) {
        this.r = r;
    }
}

四、自定义泛型接口

基本语法

interface 接口名<T,R>{
	
}
  • 接口中,静态成员也不能使用泛型
  • 泛型接口的类型,在继承接口或实现接口时确定
  • 没有指定类型,默认为Object

示例代码:

public class CustomInterfaceGeneric {
    public static void main(String[] args) {

    }
}

interface IUsb<U,R>{
    int n=10;//正常的属性赋值

//    U name="";//在接口中不允许使用泛型定义属性

    R get(U u);//普通方法中,可以使用接口泛型

    void f(R r);

    //在jdk8中,可以在接口中使用默认方法,方法中可以使用泛型
    default R method(U u){
        return null;
    }
}

//在实现接口的时候没有指定泛型的类型,系统默认为Object类型,在方法中全部用Object类型替换泛型。
class C implements IUsb{
    @Override
    public Object get(Object o) {
        return null;
    }

    @Override
    public void f(Object o) {

    }

    @Override
    public Object method(Object o) {
        return null;
    }
}

interface IA extends IUsb<String,Double>{//要在继承的时候确定泛型

}

//当我们实现IA接口时,因为IA在继承IUsb接口时,只定了U为String  R为Double
//所在实现IUsb接口的方法时,使用String替换U,使用Double替换R
class A implements IA{
    @Override
    public Double get(String s) {
        return null;
    }

    @Override
    public void f(Double aDouble) {

    }

    @Override
    public Double method(String s) {
        return null;
    }
}

五、自定义泛型方法

基本语法

修饰符<T,R...>返回类型  方法名(参数列表){
	
}
  • 泛型方法,可以定义在普通类中,也可以定义在泛型类中
  • 当泛型方法被调用时,类型会被确定
  • public void eat(E e){}修饰符后面没有<T,R…> ==>eat方法不是泛型方法,而是eat方法使用了泛型
  • 泛型类中定义泛型方法。泛型方法可以使用类声明的泛型,也可以使用自己的泛型

示例代码:

import java.util.ArrayList;

public class CustomMethodGeneric {
    public static void main(String[] args) {
        Car car=new Car();
        car.run("宝马",1000);//普通类中定义的泛型方法,在传入参数的时候,编译器会自动确定类型
        car.run(10,1000);//输入的是int型变量,编译器会自动装箱,泛型类型为Integer

        Fish<String, ArrayList> fish = new Fish<>();
        fish.eat("jack",100,new ArrayList());
    }
}

//泛型方法可以定义在普通类中,也可以定义在泛型类中
class Car{
    public void run(){//普通方法

    }
    public <T,R> void run(R r,T t){//泛型方法:注意泛型定义符的位置
        System.out.println(t.getClass());
        System.out.println(r.getClass());
    }
}

class Fish<T,R>{//泛型类
    public void run(){

    }
    public<U,M> void eat(U u,M m,R r){//泛型类中定义泛型方法。泛型方法可以使用类声明的泛型,也可以使用自己的泛型
        System.out.println(u.getClass());
        System.out.println(m.getClass());
        System.out.println(r.getClass());
    }
    public void f(T t){//f方法不是泛型方法,f方法只是使用了类声明的方法

    }
}

六、泛型的继承和通配符

1.泛型不具备继承性

List<Object> list = new ArrayList<String>();//错误,泛型不具备继承性

2.<?>:支持任意泛型

3.<? extends A>:支持A类以及A类的子类,规定了泛型的上限

4.<? super A>:支持A类以及A类的父类,不限于父类,规定了泛型的下限

示例代码:

import java.util.ArrayList;
import java.util.List;

public class GenericExtends {
    public static void main(String[] args) {
        List<Object> objectList = new ArrayList<>();
        List<String> stringList = new ArrayList<>();
        List<SA> SAList = new ArrayList<>();
        List<AA> AAList = new ArrayList<>();
        List<BB> BBList = new ArrayList<>();
        
        printCollection1(objectList);//可以传入任何类型的List
        printCollection1(stringList);
        printCollection1(AAList);
        
        printCollection2(AAList);//只能传入AA类型或AA的子类
        printCollection2(BBList);
        
        printCollection3(objectList);//只能传入AA的父类,但不限制为直接父类
        printCollection3(AAList);
//        printCollection3(BB);//错误,
        
    }
    
    public static void printCollection1(List<?> c){//任何类型都可以
    }

    public static void printCollection2(List<? extends AA> c){//任何类型都可以
    }

    public static void printCollection3(List<? super AA> c){//任何类型都可以
    }
}

class SA{ }
class AA extends SA{}
class BB extends AA{}

七、练习题

找错误

在这里插入图片描述错误:eat方法中,没有泛型标识符,为一般方法使用泛型变量,但是它使用的泛型变量在类中并没有被定义,所以错误。

泛型的综合练习

注意点:如何获取Map对象中存放的所有T对象。(遍历取出)
在这里插入图片描述
实现代码:

import org.junit.jupiter.api.Test;

import java.util.*;

public class Homework {
    public static void main(String[] args) {

    }

    @Test
    public void test(){
        DAO<User> userDAO = new DAO<>();
        userDAO.save("001",new User(1,18,"jack"));//value中存的是User对象
        userDAO.save("002",new User(2,28,"tom"));
        userDAO.save("003",new User(3,19,"smith"));

        List<User> list=userDAO.list();

        System.out.println("list:"+list);

        System.out.println("id为002的对象:"+userDAO.get("002"));

        userDAO.update("001",new User(1,56,"abc"));
        List<User> list2=userDAO.list();
        System.out.println("list2:"+list2);

        userDAO.delete("002");
        List<User> list3=userDAO.list();
        System.out.println("list3:"+list3);

    }
}

class DAO<T>{
    Map<String,T> map=new HashMap<String , T>();
    public void save(String id,T entity){//保存T对象到Map成员变量中
        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对象
        map.put(id,entity);
    }

    public List<T> list(){//返回map中存放的所有T对象
        ArrayList<T> arrayList = new ArrayList<>();

        //遍历取出map中的所有value
        for (T t : map.values()) {
            arrayList.add(t);
        }
        return arrayList;
    }

    public void delete(String id){//删除指定id对象
        map.remove(id);
    }

}

class User{
    private int id;
    private int age;
    private String name;

    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值