前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Java泛型入门介绍--自定义泛型类\方法\通配符的问题

Java泛型入门介绍--自定义泛型类\方法\通配符的问题

作者头像
阑梦清川
发布2025-02-24 15:05:33
发布2025-02-24 15:05:33
3900
代码可运行
举报
文章被收录于专栏:学习成长指南学习成长指南
运行总次数:0
代码可运行

1.基于问题引入泛型

下面的这个只展示了我们的main方法里面的内容,实际上这个java文件里面还进行了这个cat,dog类的定义,我们想要对于这个狗的名字进行打印,然后加上这个狗的年龄;

我们的一贯做法就是arrayList调用这个里面的list方法实现这个数据的插入,但是当我们的这个里面无意间错写了这个new Cat的时候,这个编译器也是不会检查出来的,我们的cat类和这个dog类之间没有任何的区别,但是这个检查又检查不出来,因为在这个增强for里面使用的是这个Object类进行接受的,因为这个向上转型的原因,因此无论我们的这个arraylist里面是什么样的数据类型,这个都是可以转换成功的;

image-20241020154837730
image-20241020154837730

这个就是我们的普通写法的弊端,如果我们不小心写错了,这个时候编译器又检查不出来,因此这个时候就会报错:

  • 列表里面插入内容的时候没有限制,我们可以随意插入任意类型的数据;
  • 增强for进行遍历的时候需要先进行向上转型,转换为这个object类型的,然后进行向下转型(转换为这个Dog类),因为我们需要对于这个dog里面的getname和getage方法进行调用;但是这个时候转型的时候cat这个不小心加进来的这个对象是无法转换成功的;

上面的两个情况就是我们的普通写法的弊端,基于这个弊端,我们设计了泛型这个语法去解决这个问题和缺陷

image-20241020155759143
image-20241020155759143

2.泛型入门介绍

基于上面出现的问题,我们可以使用泛型进行解决,泛型就是多了个尖括号,和上面的相比: 在这个尖括号里面的就是我们想要在这个数据列表里面存放的内容的类型,我们写成这个Dog>之后就不可以存放其他类型的数据,即使我们不小重新写了一个cat进去,这个时候编译器也是可以自动检查出来的;

并且我们在进行遍历的时候,节省了很多的时间,我们饿可以使用这个for(Dog)直接接收对象,直接打印,如果是之前的需要使用这个object进行这个数据的接受,然后需要进行这个强制类型转换为这个dog类型的这样先进行向上转型,然后是向下转型的操作很浪费时间,我们的泛型不需要进行转型的操作;

image-20241020160736038
image-20241020160736038

3.泛型的使用实例

下面的这个就是使用泛型:往这个hashset和hashmap里面分别进行这个数据的插入,然后就去遍历这个里面的数据内容;

其实只要我们指定了这个泛型的类型(在定义的时候),后面我们使用的时候,这个就会很容易被填充(自动填充)这个就是因为我们在定义的时候已经对于这个数据类型进行了指定;

代码语言:javascript
代码运行次数:0
复制
public class test {
    public static void main(String[] args) {
        //1.-------------对于HashSet进行泛型--------------------
        //使用泛型的方式往这个里面放进去三个学生对象
        
        //下面的这一行就是我们指定这个数据就是studeng对象
        HashSet<Student> students = new HashSet<Student>();
        students.add(new Student("jack",12));
        students.add(new Student("tom",15));
        students.add(new Student("lucy",18));
        //遍历的方式
        for (Student student : students) {
            System.out.println(student);
        }

        //2.------------对于HashMap使用泛型--------------------
        //和上面的集合结构不相同就是我们的这个地方的里面的数据都是k-v类型的,我们的这个泛型不仅需要指定这个key,也是需要指定这个value,当我们在定义的时候对于这个进行指定之后,后面就会自动填充,尤其是使用这个迭代器进行遍历的时候,这个类型就是根据我们定义的这个类型确定的;
        HashMap<String,Student> map = new HashMap<String,Student>();
        map.put("jack1",new Student("tom",25));
        map.put("jack2",new Student("tom",26));
        map.put("jack3",new Student("tom",27));
        Set<Map.Entry<String, Student>> entries = map.entrySet();
        Iterator<Map.Entry<String, Student>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Student> next =  iterator.next();
            System.out.println(next.getKey()+"-"+next.getValue());
        }
    }
}

//下面的这个就是对于类的定义,以及自动生成的这个构造器,tostring方法以及这个javabean写法内容
class Student{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

4.泛型的使用细节

  • 传入数据的类型需要是常用类,不可以是基本的数据类型;
  • 传递进去的这个不仅可以是我们的这个泛型类,也可以是这个类的子类;
  • 泛型的写法:编译器可以进行这个类型的推断
image-20241020172138659
image-20241020172138659

之前我们没有学习这个泛型的时候,这个尖括号里面的默认的类型就是Object类的,下面的这个就是使用的默认的Object,看似我们没有使用泛型,但是这个是有默认的泛型类型的;

代码语言:javascript
代码运行次数:0
复制
ArrayList objects = new ArrayList();

5.泛型的典型案例

代码语言:javascript
代码运行次数:0
复制
public class Test {
    public static void main(String[] args) {
        ArrayList<employee> employees = new ArrayList<>();
        employees.add(new employee("tom",20000,new MyDate(1000,1,1)));
        employees.add(new employee("jack",30000,new MyDate(2000,3,5)));
        employees.add(new employee("lucy",60000,new MyDate(2000,6,5)));

        System.out.println(employees);

        //下面的这个就是一个匿名内部类,依据此进行排序
        employees.sort(new Comparator<employee>() {
            @Override
            public int compare(employee o1, employee o2) {
                //先对于这个参数传入的实参进行验证
                if(!(o1 instanceof employee & o2 instanceof employee))
                {
                    System.out.println("类型不匹配~~");
                    return 0;
                }

                int i=o1.getName().compareTo(o2.getName());
                if(i!=0){
                    return i;
                }

                int yearMinus=o1.getBirthday().getYear()-o2.getBirthday().getYear();
                if(yearMinus!=0){
                    return yearMinus;
                }

                int monthMinus=o1.getBirthday().getMonth()-o2.getBirthday().getMonth();
                if(monthMinus!=0){
                    return monthMinus;
                }

                return o1.getBirthday().getDay()-o2.getBirthday().getDay();

            }
        });

        System.out.println("排序之后的结果");
        System.out.println(employees);
    }
}

class 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 String toString() {
        return "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}


class employee{
    private String name;
    private double salary;
    private MyDate birthday;

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

    public String getName() {
        return name;
    }

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

    public MyDate getBirthday() {
        return birthday;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public employee(String name, double salary, MyDate birthday) {
        this.name = name;
        this.salary = salary;
        this.birthday = birthday;
    }
}

上面的这个是在名字相同的时候,分别对于这个生日里面的年月日进行比较,但是这个在main方法里面写的话,就不够高效,不符合我们的封装性的特点,我们想要实现的效果是一次定义,永久使用,这样的话,我们每一次使用都需要把这个写一遍:

我们可以按照下面的这个方式进行优化调整,我们把这个比较的内容放到我们的这个MyDate里面去,然后再这个主方法里面就可以直接进行调用;

代码语言:javascript
代码运行次数:0
复制
public class Test {
    public static void main(String[] args) {
        ArrayList<employee> employees = new ArrayList<>();
        employees.add(new employee("tom",20000,new MyDate(1000,1,1)));
        employees.add(new employee("jack",30000,new MyDate(2000,3,5)));
        employees.add(new employee("jack",30000,new MyDate(2000,3,8)));


        System.out.println(employees);

        //下面的这个就是一个匿名内部类,依据此进行排序
        employees.sort(new Comparator<employee>() {
            @Override
            public int compare(employee o1, employee o2) {
                //先对于这个参数传入的实参进行验证
                if(!(o1 instanceof employee & o2 instanceof employee))
                {
                    System.out.println("类型不匹配~~");
                    return 0;
                }

                int i=o1.getName().compareTo(o2.getName());
                if(i!=0){
                    return i;
                }
				//具体的比较的方法在这个主方法里面,我们的这个只是进行调用即可
                //getBirthday()这个方法的返回值就是mydate
                return o1.getBirthday().compareTo(o2.getBirthday());
            }
        });

        System.out.println("排序之后的结果");
        System.out.println(employees);
    }
}

//实现这个comparable接口
class MyDate implements Comparable<MyDate>{
    private int year;
    private int month;
    private int day;

    //对于这个compare方法进行重写
    @Override
    public int compareTo(MyDate o) {
        //这个其实是this.year-o.year的过程,谁调用这个方法,谁就是this指针
        int yearMinus=year-o.getYear();
        if(yearMinus!=0){
            return yearMinus;
        }

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

        return day-o.getDay();
    }

    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 "MyDate{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}


class employee{
    private String name;
    private double salary;
    private MyDate birthday;

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

    public String getName() {
        return name;
    }

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

    public MyDate getBirthday() {
        return birthday;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public employee(String name, double salary, MyDate birthday) {
        this.name = name;
        this.salary = salary;
        this.birthday = birthday;
    }
}

6.自定义泛型类的使用

  • 泛型里面其实可以有多个类型,不只是只有一个;
  • 静态的方法和静态的属性不可以使用泛型语法;
  • 泛型的数组不可以进行初始化,就是不可以进行new操作开辟空间;
image-20241020185638198
image-20241020185638198

7.自定义泛型方法的案例

下面的这个练习主要就是想要说明一个问题:

我们下面的这个Apple里面的这个fly方法用的是自己的泛型E,虽然这个在我们的类这个定义的时候没有出现,但是这个是可以使用的,符合语法的要求;

eat和run两个自定义的方法,都是没有自己的泛型,只是这个参数里面涉及到了泛型,这个和fly方法有本质的区别,在这个访问权限修饰符后面加上这个泛型之后,我们的自定义方法才可以使用自己的这个泛型,但是eat调用的这个在我们的类定义的三个里面没有出现,就会报错,这个run使用的是m在这个类定义的时候出现了,因此这个是可以的;

image-20241020192042812
image-20241020192042812

8.泛型通配符的问题

代码语言:javascript
代码运行次数:0
复制
package demo9;

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

public class Test {
    public static void main(String[] args) {
        Object o = new String("xx");
		//泛型没有继承性:因此下面的这个无法成功编译
		//List<Object> list = new ArrayList<String>();
		//举例说明下面三个方法的使用
        
        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<AA> list3 = new ArrayList<>();
        List<BB> list4 = new ArrayList<>();
        List<CC> list5 = new ArrayList<>();
        
        
		//如果是 List<?> c , 可以接受任意的泛型类型,因此下面的这几个都是正确的
        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);
        
        
		//List<? extends AA> c: 表示 上限, 可以接受 AA 或者 AA 子类
		// printCollection2(list1);//×   object肯定不是子类
		// printCollection2(list2);//×    string和这个AA也没有任何的关系
        printCollection2(list3);//√      下面的这三个实现了继承,因此是可以的
        printCollection2(list4);//√
        printCollection2(list5);//√
        
        
		//List<? super AA> c: 支持 AA 类以及 AA 类的父类, 不限于直接父类
        printCollection3(list1);//√----------object是所有类的父类,因此这个地方是可以的
		//printCollection3(list2);//×--------string既不是这个类的父类,也不是本身,因此是不可以的
        printCollection3(list3);//√ ------AA类自己因此是可以的
		//printCollection3(list4);//×-----下面的这两个都是子类,因此是不可以的
		//printCollection3(list5);//×

    } 
    
    //? extends AA 表示 上限, 可以接受 AA 或者 AA 子类
    public static void printCollection2(List<? extends AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    } 
    
    //说明: List<?> 表示 任意的泛型类型都可以接受
    public static void printCollection1(List<?> c) {
        for (Object object : c) { // 通配符, 取出时, 就是 Object
            System.out.println(object);
        }
    } 
    
    
    //? super 子类类名 AA:支持 AA 类以及 AA 类的父类, 不限于直接父类,
    //规定了泛型的下限
    public static void printCollection3(List<? super AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }
}

class AA { }
class BB extends AA {}
class CC extends BB {}

表示 任意的泛型类型都可以接受 public static void printCollection1(List<?> c) { for (Object object : c) { // 通配符, 取出时, 就是 Object System.out.println(object); } }

代码语言:javascript
代码运行次数:0
复制
//? super 子类类名 AA:支持 AA 类以及 AA 类的父类, 不限于直接父类,
//规定了泛型的下限
public static void printCollection3(List<? super AA> c) {
    for (Object object : c) {
        System.out.println(object);
    }
}

}

class AA { } class BB extends AA {} class CC extends BB {}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.基于问题引入泛型
  • 2.泛型入门介绍
  • 3.泛型的使用实例
  • 4.泛型的使用细节
  • 5.泛型的典型案例
  • 6.自定义泛型类的使用
  • 7.自定义泛型方法的案例
  • 8.泛型通配符的问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档