当前位置: 首页 > news >正文

java泛型

引出泛型

        编写程序,在ArrayList中,添加3个dog对象,dog类含有name和age,使用get方法输出。

public class Go {public static void main(String[] args) {ArrayList list = new ArrayList();list.add(new Dog("大黄", 3));//隐式向上转型Dog放入ArrayListlist.add(new Dog("中黄", 2));list.add(new Dog("小黄", 1));for (Object o : list) {//向上转型,将ArrayList放入Object中Dog dog=(Dog)o;//向下转型,将Object o放入DogSystem.out.println(dog.getName() + "今年" + dog.getAge() + "岁了");}}
}
class Dog{private String name;private int age;//略
}

        此时如果有一个Cat类对象(与Dog类仅名称不一样),那程序还能允许吗?

public class Go {public static void main(String[] args) {ArrayList list = new ArrayList();list.add(new Dog("大黄", 3));//隐式向上转型Dog放入ArrayListlist.add(new Dog("中黄", 2));list.add(new Dog("小黄", 1));list.add(new Cat("狸花", 0));for (Object o : list) {//向上转型,将ArrayList放入Object中Dog dog=(Dog)o;//向下转型,将Object o放入DogSystem.out.println(dog.getName() + "今年" + dog.getAge() + "岁了");}}
}
class Dog{private String name;private int age;//略
}
class Cat{private String name;private int age;//略
}

        编写代码时系统并未报错,但在运行到语句 Dog dog=(Dog)o;时,系统便会报错ClassCastException,因此可见,这段程序只能接受Dog类而不能接受其他类。
        同时,系统在运行时需多次进行转型,这在处理大量数据时无疑会拖慢系统的运行速度

        这就可见这样编写代码的缺点:
1、未对程序不能处理的数据类型进行约束(不安全)
2、使用增强for循环遍历时必须将对象转型为Object,当数据量较大时,运行速度较慢(效率低)
Dog➡ArrayList➡Object➡Dog

        而泛型就可以解决这两个问题

        ArrayList<Dog> list = new ArrayList<Dog>();list.add(new Dog("大黄", 3));list.add(new Dog("中黄", 2));list.add(new Dog("小黄", 1));list.add(new Cat("狸花", 0));//报错for (Dog o : list) {System.out.println(o.getName() + "今年" + o.getAge() + "岁了");}

        这两处发生了改动

        //改动前——————————————————————————ArrayList list = new ArrayList();list.add(new Cat("狸花", 0));for (Object o : list) {Dog dog=(Dog)o;System.out.println(dog.getName() + "今年" + dog.getAge() + "岁了");}//改动后——————————————————————————ArrayList<Dog> list = new ArrayList<Dog>();//list.add(new Cat("狸花", 0));报错for (Dog o : list) {System.out.println(o.getName() + "今年" + o.getAge() + "岁了");}

1、编译时,会检查添加元素的类型,提高了安全性
2、使用增强for循环时,不再必须转型为Object,减少了类型转换的次数,提高了效率Dog➡ArrayList➡Dog
3、减少了编译时的警告,且保证了只要在编译时没有警告,运行时就不会产生ClassCastException异常

介绍

        泛型又称参数化类型,它提供了编译时类型安全检查的机制,允许在编码时指定集合中对象的类型,这样可以在编译时提供更强的类型检查,可以让我们写出更加通用、可复用的代码,同时减少类型转换以及运行时错误,并提高代码的可读性和重用性。

        类、接口、方法都可使用泛型,具体表现为在在类/接口后添加<E>,原本为数据类型的地方也替换为E,而E所代表的数据类型根据创建的实例而确定,代表在该区域相应的位置处,只能使用该数据类型。

public class Go {public static void main(String[] args) {HashMap<String, student> myMap = new HashMap<>();myMap.put("万叶",new student(18,"万叶"));myMap.put("莫娜",new student(15,"莫娜"));myMap.put("仆人",new student(22,"仆人"));myMap.put("可莉",new student(8,"可莉"));Set<Map.Entry<String, student>> set = myMap.entrySet();//myMap.entrySet().varIterator<Map.Entry<String, student>> iterator = set.iterator();//set.iterator().varwhile (iterator.hasNext()) {Map.Entry<String, student> next =  iterator.next();System.out.println(next.getKey()+""+next.getValue());}}
}
class student{private int age;private String name;//略}

注意:

1、T,E只能是引用类型,而不能是基本数据类型 

ArrayList<Integer> objects = new ArrayList<Integer>();//正确
ArrayList<int> objects = new ArrayList<int>();//错误

2、在给泛型指定具体类型后,可以传入该类型以及其子类类型 

public class Go {public static void main(String[] args) {HashSet<a> set = new HashSet<a>();set.add(new a());//正确,传入了指定类set.add(new b());//正确,传入了指定类的子类set.add(new c());//错误,传入了其他类set.add(new top());//错误,传入了指定类的父类}}
class top{}
class a extends top{}
class b extends a{}
class c{}

3、平时使用泛型时推荐采用简写形式,即第二个<>内不写内容,系统会自动将第一个<>内的内容补充到第二个<>内,
        在我们之前创建接口类的实例时,虽然没有写<>泛型,但实际上是隐藏了泛型,相当于指定泛型为Object 

        HashSet hashSet = new HashSet();//等同于HashSet<Object> hashSet2 = new HashSet<>();//等同于HashSet<Object> hashSet1 = new HashSet<Object>();

例题:

1、定义Employee类,该类包含:private成员变量name、sal、birthday,其中 birthday 为 MyDate 类的对象;
2、补充相应的get、set、构造等方法;
3、MyDate类包含:private成员变量 month、day、year,并补充相关方法
4、添加三个该类对象,并将其放入ArrayList集合中(ArrayList需使用泛型来定义)
5、对集合进行排序并输出,排序规则:调用ArratList的sort方法,传入Comparator对象(使用泛型),先按照name排序,如果name相同则按照生日的年月日先后比较

public class Go {public static void main(String[] args) {ArrayList<Employee> list = new ArrayList();list.add(new Employee("banana",8000,new MyDate(12,16,2003)));list.add(new Employee("apple",9999,new MyDate(03,21,1999)));list.add(new Employee("pear",3000,new MyDate(05,21,2002)));list.add(new Employee("apple",3000,new MyDate(03,22,1999)));list.sort(new Comparator<Employee>() {@Overridepublic int compare(Employee employee1, Employee employee2) {//先调用compareTo方法,name不同则返回正/负值,相同为0,继续下个判断int namedif= employee1.getName().compareTo(employee2.getName());if(namedif!=0)return namedif;//依次判断年月日的不同int yeardif=employee1.getBirthday().getYear()-(employee2.getBirthday().getYear());if(yeardif!=0)return yeardif;int monthdif=employee1.getBirthday().getMonth()-(employee2.getBirthday().getMonth());if(monthdif!=0)return monthdif;return employee1.getBirthday().getDay()-(employee2.getBirthday().getDay());}});Iterator<Employee> iterator = list.iterator();while (iterator.hasNext()) {Object next =  iterator.next();System.out.println(next);}}}
class Employee {private String name;private float sal;private MyDate birthday;//略}
class MyDate {private int month;private int day;private int year;//略}

执行结果:

Employee{name='apple', sal=9999.0, birthday=MyDate{month=3, day=21, year=1999}}
Employee{name='apple', sal=3000.0, birthday=MyDate{month=3, day=22, year=1999}}
Employee{name='banana', sal=8000.0, birthday=MyDate{month=12, day=16, year=2003}}
Employee{name='pear', sal=3000.0, birthday=MyDate{month=5, day=21, year=2002}} 

        但这里的代码还可以精简一下,对年月日大小的判断方法可以封装为MyDate类的一个方法

public class Go {public static void main(String[] args) {ArrayList<Employee> list = new ArrayList();list.add(new Employee("banana",8000,new MyDate(12,16,2003)));list.add(new Employee("apple",9999,new MyDate(03,21,1999)));list.add(new Employee("pear",3000,new MyDate(05,21,2002)));list.add(new Employee("apple",3000,new MyDate(03,22,1999)));list.sort(new Comparator<Employee>() {@Overridepublic int compare(Employee employee1, Employee employee2) {//先调用compareTo方法,name不同则返回正/负值,相同为0,继续下个判断int namedif= employee1.getName().compareTo(employee2.getName());if(namedif!=0)return namedif;//直接调用MyDate类内部重写的compareTo方法return employee1.getBirthday().compareTo(employee2.getBirthday());}});Iterator<Employee> iterator = list.iterator();while (iterator.hasNext()) {Object next =  iterator.next();System.out.println(next);}}}
class Employee {private String name;private float sal;private MyDate birthday;//略}
class MyDate implements Comparable<MyDate>{//实现接口,并指定泛型private int month;private int day;private int year;//略@Overridepublic int compareTo(MyDate o) {if (this.year != o.year) {// 比较年份return this.year - o.year;}if (this.month != o.month) {// 如果年份相同,比较月份return this.month - o.month;}return this.day - o.day;// 如果月份也相同,比较天,相同直接返回0}
}

泛型类

        泛型类是通过类型参数化的类,可以在创建对象的时候指定具体的类型。

public class Box<T> { // T代表数据类型,类似于int,可以有多个private T t;public void set(T t) {this.t = t;}public T get() {return t;}}Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();

 注意:

  1. 普通成员(属性,方法)可以使用泛型。
  2. 使用泛型的数组不能初始化,因为类型不确定,系统无法确定开辟多大空间,但可以先定义
  3. 静态属性/方法中不能使用类的泛型,因为静态在类加载时就已执行,但泛型的具体类型需等到创建对象时才确定,所以无法执行
  4. 泛型类的类型,是在创建对象时确定的,如果在创建对象时未指定泛型,则默认为Object
class Box<T> {private T t;//普通成员(属性,方法)可以使用泛型。public T getT() {return t;}int []arr=new int[8];//正确T []arr1;//正确,可以定义使用泛型的数组 //T []arr2=new T[8];//错误,使用泛型的数组不能初始化public static void show(){}public static int num;//正常定义静态方法、属性public static T show2(){}//错误public static T num2;//错误,静态方法/属性中不能使用类的泛型}

泛型方法

        泛型方法是在调用方法的时候指明具体的类型。泛型方法可以定义在普通类中也可以定义在泛型类中。

public class Util {public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {return p1.getKey().equals(p2.getKey()) &&p1.getValue().equals(p2.getValue());}
}

 注意:

  1. 泛型方法可以定义在普通类中也可以定义在泛型类中
    class AAA {//普通类public void show(){};//普通方法public <T,R> void show2(){}//泛型方法
    }class BBB<T,R>{//泛型类public void show(){};//普通方法public <T,R> void show2(){}//泛型方法
    }
  2. 泛型方法被调用时,方法类型会确定
    public class Go {public static void main(String[] args) {AAA aaa = new AAA();aaa.show("aaa",555);//此时T为String,R为Integeraaa.show(66.6,true);//此时T为Double,R为Boolean}}
    class AAA {public <T,R> void show(T t,R r){
    }}
  3. 没有指定类型,则默认为Object
  4. 例如public void go(T t,R r){}只是普通方法使用了泛型,而并非泛型方法,且泛型方法可使用泛型类提供的泛型,也可自定义泛型,亦或参杂使用
    class AAA <T,R>{//泛型类public void go(T t,R r){}//普通方法使用泛型public <T,K>void show(T t,K k){}//泛型方法,泛型类和泛型方法提供的泛型相结合
    }

泛型接口

        泛型也可以应用于接口。

public interface Generator<T> {T next();
}

 注意:

  1. 接口中,静态成员同样不能使用泛型
  2. 泛型接口的类型,在继承接口或实现接口时确定
  3. 没有指定类型,则默认为Object
interface usb1<T, R> {//,错误,静态变量,等同于public static final T num = 10;T num = 10;static T show3() {//错误,不能使用静态方法System.out.println("静态方法的方法体");return  null;}//正确,抽象方法(无方法体且省略abstract)T show1();//正确,普通方法(需加上default关键字)default public T show2(T t) {System.out.println("普通方法的方法体");return t;}}interface usb2 extends usb1<String,String>{}//在继承接口时确定
class class2 implements usb2{//实现类@Overridepublic String show1() {return null;}@Overridepublic String show2(String string) {return usb2.super.show2(string);}}interface usb3<S, D > extends usb1{}//在继承接口时未确定,则需也留下两个泛型
class class3 implements usb3<String,Double>{//实现接口时确定@Overridepublic Object show1() {return null;}@Overridepublic Object show2(Object o) {return usb3.super.show2(o);}}
interface usb1<T,R>{ }//错误
interface usb2 extends usb1{}
class aaa implements usb2<String,Double>{//略
}

继承通配符

1、泛型不具备继承性,之前所学中我们可以将子类对象放到父类的实例中,但在泛型中这并不适用

        Object str1 = new String();//正确ArrayList<Object> list = new ArrayList<String>();//错误

2、泛型通配符有三种
​​​       1、 <?> - 无界通配符,表示可接收任何类型的泛型
       2、<? extends Type> - 上界通配符,表类型参数必须是 Type 或 Type 的子类。
       3、<? super Type> - 下界通配符,表类型参数必须是 Type 或 Type 的父类。

        类型通配符一般是使用大写字母代替具体的类型参数。

public class Go {// 使用无界通配符的方法,可以接受任何类型的Listpublic static void printList(List<?> list) {for (Object item : list) {System.out.println(item);}}// 使用上界通配符的方法,可以接受Number或其子类型的Listpublic static double sumOfList(List<? extends Number> list) {double sum = 0.0;for (Number number : list) {sum += number.doubleValue();}return sum;}// 使用下界通配符的方法,可以接受Integer或其超类型的Listpublic static void addNumbers(List<? super Integer> list) {for (int i = 1; i <= 5; i++) {list.add(i);}}public static void main(String[] args) {// 创建不同类型的ListList<Integer> integerList = new ArrayList<>(Arrays.asList(1, 2, 3));List<Double> doubleList = new ArrayList<>(Arrays.asList(1.0, 2.0, 3.0));List<Number> numberList = new ArrayList<>();// 使用无界通配符方法打印ListSystem.out.println("Printing integerList:");printList(integerList);System.out.println("Printing doubleList:");printList(doubleList);// 使用上界通配符方法计算和System.out.println("Sum of integerList: " + sumOfList(integerList));System.out.println("Sum of doubleList: " + sumOfList(doubleList));// 使用下界通配符方法向numberList添加整数addNumbers(numberList);System.out.println("Contents of numberList after adding numbers:");printList(numberList); // 使用无界通配符方法打印结果}
}

Juit 

        JUnit 是一个用于 Java 编程语言的开源测试框架,它用于编写和运行可重复的测试。JUnit 提供了一种标准化的方式来编写测试用例,使开发人员能够验证他们的代码是否按预期工作。

使用方法:
        首次使用,在想要测试的方法本体前添加"@Test"按Alt+Enter,选择导入5.X资源包到类路径,稍等片刻。之后直接在想要测试的方法本体前添加"@Test"按Alt+Enter,单击左侧运行按钮即可运行单个方法。

例题:定义一个泛型类dao<T>,在其中定义一个Map成员变量,Map的键为String类型,值为T类型。
        分别创建以下方法:

  1. public void save(String id, T t):保存T类型的对象到Map成员变量中
  2. public T get(String id):从map中获取id对应的对象
  3. public void update(String id, T t): 替换map中key为指定id的内容,改为指定的t对象
  4. public List<T> list():返回map中存放的所有T对象
  5. public void delete(String id, T t):删除指定id对象

        定义一个User类,包含id、age、name属性,及其对应的方法。

        创建dao类的对象,分别调用五个方法来操作对象。

public class Go {public static void main(String[] args) {dao<User> D = new dao<User>();D.save("001",new User(1,18,"top"));D.save("002",new User(2,19,"mid"));D.save("003",new User(3,33,"sup"));D.save("004",new User(4,24,"jug"));D.save("005",new User(5,21,"adc"));System.out.println("全部内容——————————————————————————");for (Object o :D.list()) {System.out.println(o);}System.out.println("User内容——————————————————————————");List<User> users = D.list();for (Object o :users) {System.out.println(o);}D.update("003",new  User(3,24,"faker"));System.out.println("修改后ID为003的选手:"+D.get("003"));D.delete("005",new  User(5,21,"adc"));System.out.println("删除后内容——————————————————————————");for (Object o :D.list()) {System.out.println(o);}}}
class dao<T> {private Map<String, T> map = new HashMap<>();public void save(String id, T t) {map.put(id, t);}public T get(String id) {return map.get(id);}public void update(String id, T t) {map.put(id, t);}public List<T> list() {return new  ArrayList<T>(map.values());}public void delete(String id, T t) {map.remove(id, t);}@Overridepublic String toString() {return "dao{" +"map=" + map +'}';}
}
class User {private int id;private int age;private String name;//略
}

原文地址:https://blog.csdn.net/qq_65501197/article/details/142663062
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mrgr.cn/news/45799.html

相关文章:

  • 【java数据结构】ArrayList实例
  • webpack学习
  • java8 双冒号(::)使用方法
  • 【C++ 11】for 基于范围的循环
  • 以后再也不要说程序员不能拿诺贝尔了
  • CentOS7 虚拟机操作系统安装及相关配置教程
  • 算法:238.除自身以外数组的乘积
  • 简易CPU设计入门:取指令(四)
  • 1打家劫舍三部曲
  • Java中Collections类详解
  • Git管理远程仓库
  • 【动态规划-最长公共子序列(LCS)】【hard】力扣1092. 最短公共超序列
  • rust gio-rs 挂载 samba 磁盘
  • 【Java 并发编程】多线程安全问题(上)
  • 算法:724.寻找数组的中心下标
  • 面试(十)
  • 【JVM】内存分析工具JConsole/Visual VM
  • 每日OJ题_牛客_平方数_数学_C++_Java
  • 面试题:Redis(一)
  • 回到原点再出发