欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > java学习笔记-泛型(韩顺平)

java学习笔记-泛型(韩顺平)

2025/6/5 21:10:47 来源:https://blog.csdn.net/2301_79070677/article/details/141871748  浏览:    关键词:java学习笔记-泛型(韩顺平)

1. 泛型的理解和好处

  • 看一个需求

    1. 请编写程序,在ArrayList中,添加3个Dog对象

    2. Dog对象含有name和age,并输出name和age(要求使用getXxx())

      传统方式

      public class Generics_ {@SuppressWarnings({"all"})public static void main(String[] args) {ArrayList arrayList = new ArrayList();arrayList.add(new Dog("DaHuang",3));arrayList.add(new Dog("XiaoHua",4));arrayList.add(new Dog("TuanTuan",2));for (int i = 0; i < arrayList.size(); i++) {Dog d = (Dog)arrayList.get(i);//假如程序员不小心添加了一只猫,会抛出ClassCastException异常System.out.println(d.getName() + "-" + d.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;}
      ​@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +'}';}
      }

      传统方法问题分析:

      1. 不能对加入到集合ArrayList中的数据类型进行约束(不安全)

      2. 遍历的时候需要进行类型转换,如果集合中数据量较大,对效率有影响

      泛型快速体验-用泛型解决前面的问题

      package com.mdklea.generics;
      ​
      ​
      import java.util.ArrayList;
      ​
      public class Generics02 {public static void main(String[] args) {//解读//1. 当我们 ArrayList<GDog> 表示存放到 ArrayList 集合中的元素是Dog类型//2. 如果编译器发现添加的类型不满足要求就会报错//3. 在遍历的时候可以直接取出GDog而不是ObjectArrayList<GDog> dogs = new ArrayList<GDog>();dogs.add(new GDog("DaHuang",2));dogs.add(new GDog("XiaoHua",7));dogs.add(new GDog("XiaoMei",5));for (GDog dog :dogs) {System.out.println(dog.getName() + "-" + dog.getAge());}
      ​}
      }
      class GDog {private String name;private int age;
      ​public GDog(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;}
      ​@Overridepublic String toString() {return "GDog{" +"name='" + name + '\'' +", age='" + age + '\'' +'}';}
      }

  • 泛型的好处

    1. 编译时,检查添加元素的类型,提高了安全性

    2. 减少了类型转换的次数,提高效率

    3. 不再显示编译警告

2.泛型介绍

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

  1. 泛型又称为参数化类型,是JDK5.0出现的新特性,解决数据类型的安全性问题

  2. 在类声明或实例化时只要指定好需要的具体的类型即可

  3. java泛型可以保证如果程序在编译时没有发出警告,运行就不会产生ClassCastException异常。同时,代码更加简洁、健壮

  4. 泛型的作用是:可以在类声明时通过一个标识符表示类中某个属性的类型,或者是某个方法返回值的类型,或者是参数类型。

  5. ss可以理解为一种能够接收数据类型的数据类型

    package com.mdklea.generics;
    ​
    public class Generics03 {public static void main(String[] args) {Person<String> stringPerson = new Person<String>("m1");System.out.println(stringPerson.f());}
    }
    ​
    class Person<E>{E s; //E表示 s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型
    ​public Person(E s) {  //E也可以是参数类型this.s = s;}
    ​public E f() {  //E也可以是返回类型return s;}
    }

  6. 特别强调:E具体的数值类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型

3.泛型的语法

  • 泛型的声明

    interface 接口<T>{} 和class 类<K,V>{}

    //比如List,ArrayList

    1. 其中,K,T,V不代表值,而是表示类型。

    2. 任意字母都可以。常用T表示,是Type的缩写

  • 泛型的实例化

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

    1. List<String> strList = new ArrayList<String>();

    2. Iterator<Customer> iterator = customers.iterator();

​
package com.mdklea.generics;
​
import java.util.*;
​
public class GenericExercise {@SuppressWarnings({"all"})public static void main(String[] args) {HashMap<String, Student> studentHashMap = new HashMap<>();HashSet<Student> studentHashSet = new HashSet<>();Student m1 = new Student("马1", 700);Student m2 = new Student("马2", 720);Student xxp = new Student("xxp", 730);studentHashMap.put(m1.getName(),m1);studentHashMap.put(m2.getName(),m2);studentHashMap.put(xxp.getName(),xxp);Set<Map.Entry<String, Student>> entries = studentHashMap.entrySet();for (Map.Entry entry :entries) {System.out.println(entry.getKey() + "-" + entry.getValue());}Set<String> set = studentHashMap.keySet();System.out.println("==============================");for (String stu :set) {System.out.println(stu + studentHashMap.get(stu));}System.out.println("======================================");studentHashSet.add(new Student("XiaoWang",583));studentHashSet.add(new Student("XiaoMei",432));studentHashSet.add(new Student("XiaoYe",283));for (Student stu :studentHashSet) {System.out.println(stu);}
​System.out.println("itit");Iterator<Student> iterator = studentHashSet.iterator();while (iterator.hasNext()) {Student next = iterator.next();System.out.println(next);}}
}
class Student {private String name;private int grade;
​public Student(String name, int grade) {this.name = name;this.grade = grade;}
​public String getName() {return name;}
​public void setName(String name) {this.name = name;}
​public int getGrade() {return grade;}
​public void setGrade(int grade) {this.grade = grade;}
​@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", grade=" + grade +'}';}
}​

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

  1. interface List <T>{},Public class HashSet<E> {}...等等

    说明:T,E只能是引用类型

    看看下面语句是否正确?

    List <Integer> list = new ArrayList<Integer> {}; // ok

    List<int>list2 = new ArrayList<int>{}; // 错误

  2. 在指定泛型具体类型后,可以传入该类型或者其子类类型

  3. 泛型使用形式

    List<Integer> list1 = new ArrayList<Integer> {};

    List<Integer>list2 = new ArrayList<> {};

    如果我们这样写 List list3 = new ArrayList(); 默认给它的泛型就是[<E> E就是Object]

5.课堂练习

package com.mdklea.generics;
​
import java.util.ArrayList;
import java.util.Comparator;
​
public class GenericExercise01 {@SuppressWarnings({"all"})public static void main(String[] args) {ArrayList<Employee> employees = new ArrayList<>();employees.add(new Employee("mda",30000,new Employee().OMyDate(6,10,2000)));employees.add(new Employee("mda",30000,new Employee().OMyDate(21,10,2000)));employees.add(new Employee("mda",30000,new Employee().OMyDate(2,10,2000)));employees.sort(new Comparator<Employee>() {@Overridepublic int compare(Employee o1, Employee o2) {//比较名字int i = o1.getName().compareTo(o2.getName());//可以不写那么多elseif (i != 0){return i;}else {i = (int)(o1.getSal() - o2.getSal());if (i != 0){return i;}else {//把年月日的写法写到MyDate中return o1.getBirthday().compareTo(o2.getBirthday());}}}});for (Employee e :employees) {System.out.println(e);}
​}
}
​
@SuppressWarnings({"all"})
class Employee {private String name;private double sal;
​private MyDate birthday;
​public Employee(String name, double sal, MyDate birthday) {this.name = name;this.sal = sal;this.birthday = birthday;}
​public Employee() {}//不写内部类貌似更方便class MyDate implements Comparable<MyDate>{private int day;private int month;private int year;
​public MyDate(int day, int month, int year) {this.day = day;this.month = month;this.year = year;}
​public int getDay() {return day;}
​public void setDay(int day) {this.day = day;}
​public int getMonth() {return month;}
​public void setMonth(int month) {this.month = month;}
​public int getYear() {return year;}
​public void setYear(int year) {this.year = year;}
​
​
​@Overridepublic String toString() {return day +"-" + month +"-" + year ;}
​
​@Overridepublic int compareTo(MyDate o) {int yearMinus = this.year - o.year;if (yearMinus != 0){return yearMinus;}int monthMinus = this.month - o.month;if (monthMinus != 0){return monthMinus;}int dayMinus = this.day - o.day;return dayMinus;}}
​public String getName() {return name;}
​public void setName(String name) {this.name = name;}
​public double getSal() {return sal;}
​public void setSal(double sal) {this.sal = sal;}
​public MyDate getBirthday() {return birthday;}
​public void setBirthday(MyDate birthday) {this.birthday = birthday;}
​@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", sal=" + sal +", birthday=" + birthday +'}';}public MyDate OMyDate(int day,int month,int year){return new MyDate(day,month,year);}
}

6.自定义泛型

  • 基本语法

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

    //...表示可以有多个成员

    }

  • 注意细节

    1. 普通成员可以使用泛型(属性或方法)

    2. 使用泛型的数组不能初始化

    3. 静态方法中不能使用类的泛型

    4. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定泛型)

    5. 如果在创建对象时,没有指定泛型,默认为Object

  • package com.mdklea.customgeneric;
    ​
    public class CustomGeneric_ {public static void main(String[] args) {
    ​}
    }
    //解读
    //1. Tiger后面有泛型,所以我们把 Tiger称为自定义泛型类
    //2.T R M 是泛型的标识符,一般是单个的大写字母
    //3.泛型的标识符可以有多个
    //4.普通成员可以使用泛型(属性,方法)
    //5.使用泛型的数组,不能初始化
    //6.静态方法不能使用类的泛型
    class Tiger<T,R,M> {String name;R r;//属性使用泛型M m;T t;
    ​//因为数组在创建时不能确定T的类型,就无法在内存开空间//T[] ts = new T[4];T[] ts;//因为静态是和类相关的,在类加载时,对象还没有创建//所以如果静态方法和静态属性使用了泛型,JVM就无法初始化//public static void m1(M m){//}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;}
    }
  • 自定义泛型接口

    • 基本语法

      interface 接口名<T,R,...>{ }

    • 注意细节

      1. 接口中,静态成员也不能使用泛型(这个和泛型类的规定一样)

      2. 泛型接口的类型,在继承接口或者实现接口时确定

      3. 没有指定类型,默认为Object

    package com.mdklea.customgeneric;
    ​
    public class CustomInterfaceGeneric {public static void main(String[] args) {
    ​}
    }
    /*
    泛型接口使用的说明
    1. 接口中,静态成员不能使用泛型
    2. 泛型接口的类型,是在继承接口或者实现接口时确定的
    3. 没有指定类型,默认为Object*/
    interface IUsb<U,R> {//接口中的成员都是静态性质的int i = 10;
    ​//U name; 不能这样使用
    ​//普通方法中,可以使用接口泛型R get(U u);void hi(R r);void run(R r1,R r2,U u1,U u2);
    ​//JDK8中,可以在接口中,使用默认方法,也是可以使用泛型的default R methdod(U u) {return null;}
    }
    //在继承接口时,确定泛型接口类型
    interface IA extends IUsb<String ,Double> {}
    ​
    //当我们实现IA接口时,因为IA在继承IUsb 接口时,指定了 U为String R为Double
    //在实现IUsb接口的方法时,使用String替换U,Double替换R
    class AA implements IA {
    ​@Overridepublic Double get(String s) {return null;}
    ​@Overridepublic void hi(Double aDouble) {
    ​}
    ​@Overridepublic 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> {@Overridepublic Float get(Integer integer) {return null;}
    ​@Overridepublic void hi(Float aFloat) {
    ​}
    ​@Overridepublic void run(Float r1, Float r2, Integer u1, Integer u2) {
    ​}
    }
    //没有指定类型,默认为Object
    //建议直接写成class CC implements IUsb<Object,Object>{}
    class CC implements IUsb { // 等价 class CC implements IUsb<Object,Object>{}
    ​@Overridepublic Object get(Object o) {return null;}
    ​@Overridepublic void hi(Object o) {
    ​}
    ​@Overridepublic void run(Object r1, Object r2, Object u1, Object u2) {
    ​}
    }

  • 自定义泛型方法

    • 基本语法

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

    • 注意细节

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

      2. 当泛型方法被调用时,类型会确定

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

    • package com.mdklea.customgenericmethod;
      ​
      ​
      import java.util.ArrayList;
      ​
      @SuppressWarnings({"all"})
      public class CustomGenericMethod {public static void main(String[] args) {Car car = new Car();//当泛型方法被调用时,类型会确定car.fly("宝马",100);Fish<String, ArrayList> fish = new Fish<>();fish.hello(new ArrayList(),12.4f);}
      }
      ​
      /*
      泛型方法的使用:1. 泛型方法可以定义在普通类中,也可以定义在泛型类中2. 当泛型方法被调用时,类型会确定3. public void eat(E e) {} 修饰符后没有<T,R>eat方法不是泛型方法,而是使用了泛型。*/
      class Car {public void run() {//普通方法
      ​}//泛型方法可以定义在普通类中,也可以定义在泛型类中public<T,R> void  fly(T t,R r) {//泛型方法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) { //泛型方法
      ​}
      ​//说明//1. 下面这个方法不是泛型方法//2. 是hi方法使用了类声明的 泛型public void hi(T t) {}//泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型public<K> void hello(R r,K k) {System.out.println(r.getClass());System.out.println(k.getClass());}
      }

7.泛型的继承和通配符

  • 泛型的继承和通配符说明:

    1. 泛型不具备继承性

      List<Object> list = new ArrayList<String> (); //对吗?

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

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

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

8.JUnit单元测试类

  • 为什么需要JUnit

    1. 一个类有很多功能代码需要测试,为了测试,就需要写到main方法中

    2. 如果有多个功能代码测试,就需要来回注销,切换很麻烦

    3. 如果可以直接运行一个方法,就方便很多,并且可以给出相关信息就好了 -> Junit

  • 基本介绍

    1. JUnit是一个Java语言的单元测试框架

    2. 多少java的开发环境都已经集成了JUnit作为单元测试的工具

8.章节练习

package com.mdklea.homework;
​
import org.junit.jupiter.api.Test;
​
import java.util.*;
​
@SuppressWarnings({"all"})
public class HomeWork01 {public static void main(String[] args) {
​}@Testpublic void testList() {DAO<User> dao = new DAO<>();dao.save("001",new User(1,10,"jack"));dao.save("002",new User(2,20,"mary"));dao.save("003",new User(3,30,"smith"));System.out.println(dao.list());dao.update("003",new User(3,20,"mdk"));System.out.println(dao.list());dao.delete("001");System.out.println(dao.list());}
}
@SuppressWarnings({"all"})
class DAO<T> {private 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) {if (map.containsKey(id)){map.put(id,entity);}else {System.out.println("map中无此对象!");}}
​public List<T> list() {Collection<T> values = map.values();return new ArrayList<>(values);}
​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;}
​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;}
​@Overridepublic String toString() {return "User{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词