首页 > 编程语言 >Java 泛型

Java 泛型

时间:2023-03-25 20:46:14浏览次数:33  
标签:Java name int void 泛型 new public

泛型

1. 泛型引入

  1. 不能对加入到 集合 ArrayList中的数据类型进行约束(不安全)
  2. 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
package generic_;

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

        //  使用泛型
        //  老韩解读
        //  1)当我们 ArrayList<Dog> 表示存放到 ArrayList 集合中的元素是 Dog 类型
        //	2) 如果编译器发现添加的类型,不满足要求,就会报错
        //	3) 在遍历的时候,可以直接取出 Dog 类型,而不是 Object 
        ArrayList<Dog> arrayList = new ArrayList<Dog>();
        arrayList.add(new Dog("旺财", 10));
        arrayList.add(new Dog("发财", 1));
        arrayList.add(new Dog("小黄", 5));

        //  假如我们一不小心,添加了一只猫
//        arrayList.add(new Cat("招财猫", 8));

        for (Dog dog : arrayList) {
            System.out.println(dog.getName() + '-' + dog.getAge());
        }
    }
}
class Dog {
    private String name;
    private int age;

    public Dog(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;
    }
}

class Cat {
    private String name;
    private int age;

    public Cat(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;
    }
}

image-20230324153233750

2. 泛型语法

理解:泛(广泛)型(类型) ===> Integer、String、Dog

  1. 泛型又称为参数化类型,是 Jdk1.5出现的新特性,解决数据类型安全性问题
  2. 在类声明或实例化时,只要指定好需要的具体的类型即可
  3. Java 泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException 异常,同理,代码更阿吉简洁,健壮
  4. 泛型的作用:可以在类声明时通过一个标识表示
    • 类中某个属性的类型
    • 或者是某个方法的返回值的类型
    • 参数类型
//	E:泛型
public class ArrayList<E> extends AbstractList<E>
package generic_;

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

        Person<String> stringPerson = new Person<String>("爱新觉罗LQ");
        stringPerson.print_type();
    }
}

class Person<E> {
    E s;    //  E 表示 s的数据类型,该数据类型在定义 Person 对象的时候指定,即在编译期间,就确定E是什么类型

    public Person(E s) {    //  E 也可以是参数类型
        this.s = s;
    }

    public E f() {  //  返回类型使用 E
        return s;
    }

    public void print_type(){
        System.out.println(s.getClass());   //  显示 s 的运行类型
    }
}
# 运行类型
class java.lang.String

3. 泛型的声明

  • inteface 接口 <T>{} 和 class 类 <K, V>{},比如:List,ArrayList
  • 说明:
    • 其中,T、K、V 不代表值,而是表示类型
    • 任意字母都可以,常用 T 表示,是 Type 的简写

4. 泛型的实例化

要在类名后面指定类型参数的值(类型),如:

  • List<String> strList = new ArrayList<String>();
  • Iterator<Customer> iterator = customers.iterator();
package generic_;

public class Test1 {
    public static void main(String[] args) {
        //  使用泛型方式给HashSet 放入 3个学生对象
        HashSet<Student> students = new HashSet<Student>();

        Student<String> student1 = new Student<>("小明");
        Student<String> student2 = new Student<>("小赵");
        Student<String> student3 = new Student<>("小李");

        students.add(student1);
        students.add(student2);
        students.add(student3);

        //  增强 for 循环 (I)
        for (Student student : students) {
            System.out.println(student);
        }

        System.out.println("==========");

        //  迭代器 (itit)
        Iterator<Student> iterator = students.iterator();
        while (iterator.hasNext()) {
            Student next = iterator.next();
            System.out.println(next);
        }

        System.out.println("==========");

        //  使用 HashMap(k, V) 【key :String,V:Student】
        HashMap<String, Student> studentHashMap = new HashMap<String, Student>();

     /*   public class HashMap<K,V>*/


        studentHashMap.put("小明", new Student("小明"));
        studentHashMap.put("小赵", new Student("小赵"));
        studentHashMap.put("小李", new Student("小李"));

        //  迭代器 EntrySet

      /*  public Set<Map.Entry<K,V>> entrySet() {
            Set<Map.Entry<K,V>> es;
            return (es = entrySet) == null ? (entrySet = new HashMap.EntrySet()) : es;
        }*/

        Set<Map.Entry<String, Student>> entries = studentHashMap.entrySet();

      /*  public final Iterator<Map.Entry<K,V>> iterator() {
            return new HashMap.EntryIterator();
        }*/

        Iterator<Map.Entry<String, Student>> iterator1 = entries.iterator();

        while (iterator1.hasNext()) {
            Map.Entry<String, Student> next = iterator1.next();
            //  因为它是一个entry,所以可以调用相应的方法, 这里的 Value 就是我们的学生对象了
            System.out.println(next.getKey() + "-" + next.getValue());
        }

    }
}

class Student<E> {

    E name;

    public Student(E name) {
        this.name = name;
    }

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

5. 泛型使用细节

  1. T、E 只能是引用类型

    //	不能是基本数据类型
    Type argument cannot be of primitive type
    
  2. 在指定泛型具体类型后,可以传入该类型或者其子类型

  3. 泛型使用形式

    List<Integer> list1 = new ArrayList<Integer>();
    //	可以简写
    List<Integer> list2 = new ArrayList<>();
    
  4. 如果写成这样

    List list3 = new ArrayList() //	默认它的泛型是 Object
    

6. 匿名内部类

7. 回顾 Arrays.sort() 方法

Arrays.sort(arr, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;
    }
}); 

调用定制排序时,需要传入 2个参数:

  • 待排序数组 arr

  • 实现了 Comparator接口的匿名内部类,要求实现 compare() 方法

    new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }
    
  • 体现 接口编程 的方式

    njnj

集合冒泡 + 定制 Sort

package com.kko;

public class ArraysSortCustom {

    public static void main(String[] args) {
        int[] arr = {1, -1, 8, 0, 20};
        bubbleSort(arr, new Comparator() {	//	定制冒泡排序
            @Override
            public int compare(Object o1, Object o2) {
                return (Integer)o1 - (Integer)o2;
            }
        });
        System.out.println(Arrays.toString(arr));
    }
    //  冒泡!!!
    public static void bubbleSort(int[] arr, Comparator comparator){ 
        for (int i = arr.length - 1; i >= 1; i--) {
            for (int j = 0; j < i; j++) {
                //  数组排序由这个方法返回的值决定
                if (comparator.compare(arr[j], arr[j + 1]) > 0){
                    swap(arr, j, j + 1);
                }
            }
        }
    }

    public static void swap(int[] arr, int i, int j){
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

8. 定义排序方式【⭐】

泛型课堂练习【排序方式】

  • 使用 ArrayList的 sort 方法
  • 传入 Comparator 对象【使用泛型】---> 定制排序
    • 先按照 name 排序
    • 如果 name 相同,则按照 生日日期先后排序
package com.kko;

public class Test {
    @org.junit.Test
    public void test(){
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee("alice", 2000,  new MyDate(2000, 7, 1)));
        employees.add(new Employee("zack", 5000,  new MyDate(2002, 5, 4)));
        employees.add(new Employee("bob", 7000,  new MyDate(2001, 6, 6)));
        for (Employee employee : employees) {
            System.out.println(employee);
        }

        System.out.println();
        System.out.println("对雇员进行排序!!!");
        System.out.println();


        Collections.sort(employees, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                if (!(o1 != null && o2 != null)){
                    return 0;
                }
                //  比较 name
                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;
                }
                int dayMinus = o1.getBirthday().getDay() - o2.getBirthday().getDay();
                return dayMinus;
            }
        });

        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
}

由于比较代码主要是比较 MyDate,所以将代码剪切到 MyDate 类中!!!

package com.kko;
//	实现 Comparable<T> 接口,并指定泛型 MyDate
public 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 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{" +
                "year=" + year +
                ", month=" + month +
                ", 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();
    }
}
package com.kko;

public class Test {
    @org.junit.Test
    public void test(){
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee("alice", 2000,  new MyDate(2000, 7, 1)));
        employees.add(new Employee("zack", 5000,  new MyDate(2002, 5, 4)));
        employees.add(new Employee("bob", 7000,  new MyDate(2001, 6, 6)));
        for (Employee employee : employees) {
            System.out.println(employee);
        }

        System.out.println();
        System.out.println("对雇员进行排序!!!");
        System.out.println();


        Collections.sort(employees, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                if (!(o1 != null && o2 != null)){
                    return 0;
                }
                //  比较 name
                int i = o1.getName().compareTo(o2.getName());
                if (i != 0){
                    return i;
                }
                return o1.getBirthday().compareTo(o2.getBirthday());
            }
        });

        for (Employee employee : employees) {
            System.out.println(employee);
        }
    }
}

9. 自定义泛型类

  1. 基本语法

    class 类名<T,,R...>{

    ​ 成员;

    }

注意细节:

  • 普通成员可以使用泛型(属性、方法)
  • 使用泛型的数组,不能初始化
  • 静态方法中不能使用类的泛型 ===> 【因为静态是和类相关的,在类加载时,对象还未创建】
  • 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
  • 如果在创建对象时,没有指定类型,默认为 Object
package generic_;

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

    }
}

//  解读
//  1)Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
//  2)T,R,M :泛型的标识符,一般为 单个大写字母
//  3)泛型标识符可以有多个
//  4)普通成员可以使用泛型(属性,方法)
class Tiger<T, R, M> {
    String name;
    R r;    //  属性使用到泛型
    M m;
    T t;


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

    //  方法使用泛型、

    public String getName() {
        return name;
    }

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

    public R getR() {
        return r;
    }

    public void setR(R r) { //  方法使用到泛型
        this.r = r;
    }

    public M getM() {   //  返回类型可以使用到泛型
        return m;
    }

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

    public T getT() {
        return t;
    }

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

泛型数组不能初始化,因为它的类型还没有确定,是不知道开多大空间的

image-20230325111238013

10. 自定义泛型接口【接口中的成员都是静态的】

语法:

inteface 接口名<T, R...>{

}

注意细节:

  1. 接口中,静态成员也不能使用泛型
  2. 泛型接口的类型,在 继承接口 或者 实现接口 时确定
  3. 没有指定类型,默认是 Object
package generic_;

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

    }
}
/**
 * 泛型接口使用的说明
 * 1. 接口中,静态成员也不能使用泛型
 * 2. 泛型接口的类型, 在继承接口或者实现接口时确定
 * 3. 没有指定类型,默认为Object
 */
//在继承接口指定泛型接口的类型
interface IA extends IUsb<String, Double> {
}
//  当我们去实现IA 接口时,因为IA 在继承IUsb 接口时,指定了U 为String R 为 Double

//  在实现IUsb 接口的方法时,使用String 替换 U, 是Double 替换 R

class AA implements IA {
    @Override
    public Double get(String s) {
        return null;
    }

    @Override
    public void hi(Double aDouble) {
    }

    @Override
    public void run(Double r1, Double r2, String u1, String u2) {
    }
}

//实现接口时,直接指定泛型接口的类型
//给U 指定Integer 给R 指定了Float
//所以,当我们实现IUsb 方法时,会使用Integer 替换U, 使用Float 替换R
class BB implements IUsb<Integer, Float> {
    @Override
    public Float get(Integer integer) {
        return null;
    }

    @Override
    public void hi(Float aFloat) {
    }

    @Override
    public void run(Float r1, Float r2, Integer u1, Integer u2) {
    }
}

//没有指定类型,默认为Object
//建议直接写成IUsb<Object,Object>
class CC implements IUsb { //等价class CC implements IUsb<Object,Object> {
    @Override
    public Object get(Object o) {
        return null;
    }

    @Override
    public void hi(Object o) {
    }

    @Override
    public void run(Object r1, Object r2, Object u1, Object u2) {
    }
}

interface IUsb<U, R> {
    int n = 10;

    //U name; 不能这样使用 ---> 接口中成员都是静态的 
    
//普通方法中,可以使用接口泛型
    R get(U u);

    void hi(R r);

    void run(R r1, R r2, U u1, U u2);

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

image-20230325121911408

  • 继承接口时,指定泛型类型
  • 实现接口时,指定泛型类型
  • 没有指定泛型类型,默认为 Object

11. 自定义泛型方法

语法:

修饰符<T, R> 返回类型 方法名(参数列表){

}

注意细节:

  1. 可以定义在普通类,也可以定义在泛型类中

    //  泛型方法可以定义在普通类中,也可以定义在泛型类中
    class Car{  //  普通类
        public void run(){  //  普通方法
    
        }
    
        //  1. <T, R> 就是泛型标识符
        //  2. 是提供给 fly 方法使用的
        public<T, R> void fly(T t, R r){  //  泛型方法
    
        }
    }
    
    class Fish<T, R>{   //  泛型类
        public void run(){  //  普通方法f
    
        }
    
        public<U, M> void eat(U u, M m) {   //  泛型方法
    
        }
    
    }
    
  2. 当泛型方法被调用时,类型会确定

    //  当调用方法时,传入参数,编译器就会确定类型
    new Car().fly("宝马", 100);
    
    public<T, R> void fly(T t, R r){  //  泛型方法
        System.out.println(t.getClass());
        System.out.println(r.getClass());
    }
    

    image-20230325123640360

  3. public void eat(E e){},修饰符 后没有 <T, R...>,eat 方法不是泛型方法,而是使用了泛型

  4. 泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型

    image-20230325123950447

课后习题:

image-20230325125314017

12. 泛型继承和通配符【⭐】

  1. 泛型不具有继承性
  2. <?>:支持任意泛型
  3. <? extends A>:支持 A类以及 A类的子类,规定了泛型的 上限
  4. <? super A>:支持 A类以及A类的父类,不限于直接父类,规定了泛型的 下限
package dkdld;

public class GenericExtends {
    public static void main(String[] args) {
        ArrayList<Object> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<AA> list3 = new ArrayList<>();
        ArrayList<BB> list4 = new ArrayList<>();
        ArrayList<CC> list5 = new ArrayList<>();
    }

    //  说明:
    //  List<?> 表示任意的泛型类型都可以接收
    public static void printCollection1(List<?> c){
        for (Object o : c) {
            System.out.println(o);
        }
    }

    //  ? extends AA 表示上限,可以接收 AA 或者 AA 子类

    public static void printCollection2(List<? extends AA> c){
        for (AA aa : c) {
            System.out.println(aa);
        }
    }

    //  ? super 子类类名AA:支持 AA类以及 AA类子类的父类,不限于直接父类
    //  规定了泛型的下限
    public static void printCollection3(List<? super AA> c){
        for (Object o : c) {
            System.out.println(o);
        }
    }
}

class AA{

}

class BB extends AA{

}
class CC extends BB{

}

13. 课后作业

  • 泛型类
  • 方法中使用泛型

image-20230325160838214

package home;

public class DAO<T> {   //  泛型类

    Map<String, T> map = new HashMap<>();

    public void save(String id, T entity){
        map.put(id, entity);
    }


    public T get(String id){
        return map.get(id);
    }

    public void update(String id, T entity){
        map.put(id, entity);
    }

    public List<T> list(){
        List<T> ts = new ArrayList<>();
        for (T t : map.values()) {
            ts.add(t);
        }
        return ts;
    }

    public void delete(String 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 + '\'' +
                '}';
    }
}

测试一下:

package home;

public class test {

    @Test
    public void test(){
        //  DAO<User> 确保 DAO类中的属性
        //
        //  Map<String, T> map = new HashMap<>(); 中的 T 为 User
        //
        DAO<User> userDAO = new DAO<>();
        userDAO.save("1", new User(1, 14, "jack"));
        userDAO.save("2", new User(2, 15, "Mary"));
        userDAO.save("3", new User(3, 16, "Alice"));
        userDAO.save("4", new User(4, 17, "Bob"));
        userDAO.save("5", new User(5, 18, "Zeus"));

        List<User> list = userDAO.list();
        for (User user : list) {
            System.out.println(user);
        }

        System.out.println("====================");

        userDAO.delete("1");

        //  删除元素后,需要重新获取 list
        list = userDAO.list();
        for (User user : list) {
            System.out.println(user);
        }

        System.out.println("====================");

        userDAO.update("2", new User(77, 18, "Kobe"));
        list = userDAO.list();
        for (User user : list) {
            System.out.println(user);
        }
    }
}

标签:Java,name,int,void,泛型,new,public
From: https://www.cnblogs.com/aclq/p/17255539.html

相关文章

  • java——spring boot集成kafka——kafka中主题topic和分区partition的概念
                                                       ......
  • java——spring boot集成kafka——查看消费组以及信息
                                                       ......
  • java——spring boot集成kafka——单播与多播消息的实现
    单播消息的实现:   单播消息:⼀个消费组⾥只会有⼀个消费者能消费到某⼀个topic中的消息。于是可以创建多个消费者,这些消费者在同⼀个消费组中。  创建一个消费......
  • Java多线程
    一、多线程简介 Java多线程是指在一个程序中同时执行多个线程(线程就是一条执行路径)。Java中的多线程可以提高程序的运行效率和并发性,通常用于执行一些耗时的操作或需要同......
  • Java获取系统时间的4种方法
    Java获取系统时间的4种方法1publicDateTime(){2//1.设置时间格式3/*4*yyyy-MM-dd:年-月-日2023-03-255......
  • Java IO详解
    一、IO简介1.概念 JavaIO(Input/Output)是Java编程语言对输入和输出(I/O)进行封装的一组类和接口,JavaIO提供了输入流和输出流的抽象,可以使开发者以相同的方式读写......
  • 使用 Docker 打包 Java 应用程序为镜像
    创建Dockerfile在Docker中,Dockerfile是一个文本文件,包含一系列的指令,用于定义如何构建Docker镜像。可以通过编写Dockerfile来自定义Docker镜像。对于Java应用......
  • java——spring boot集成kafka——单节点示例
    首先安装一个zk。然后再安装kafka:   执⾏以下命令创建名为“test”的topic,这个topic只有⼀个partition,并且备份因⼦也设置为1: 然后在kafka节点下,执行如下命令:......
  • Java进阶基础知识点(包及final关键字、常量)
    一:包的概述包就是文件夹,用来管理各种不同功能的Java类,方便后期代码维护。包的命名规则:公司域名的反写+包的作用,需要全部英文小写,见名知意。例如;com.51cto.domain.student.......
  • JavaSE
    前言字节流和字符流体系相同,仅操作的数据类型不同。节点流和过滤流的关系,是装饰者模式的体现。转换流,是适配器模式的体现。I/O对磁盘文件的读写,针对的是File类。......