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>();
注意:
- 普通成员(属性,方法)可以使用泛型。
- 使用泛型的数组不能初始化,因为类型不确定,系统无法确定开辟多大空间,但可以先定义
- 静态属性/方法中不能使用类的泛型,因为静态在类加载时就已执行,但泛型的具体类型需等到创建对象时才确定,所以无法执行
- 泛型类的类型,是在创建对象时确定的,如果在创建对象时未指定泛型,则默认为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());}
}
注意:
- 泛型方法可以定义在普通类中也可以定义在泛型类中
class AAA {//普通类public void show(){};//普通方法public <T,R> void show2(){}//泛型方法 }class BBB<T,R>{//泛型类public void show(){};//普通方法public <T,R> void show2(){}//泛型方法 }
- 泛型方法被调用时,方法类型会确定
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){ }}
- 没有指定类型,则默认为Object
- 例如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();
}
注意:
- 接口中,静态成员同样不能使用泛型
- 泛型接口的类型,在继承接口或实现接口时确定
- 没有指定类型,则默认为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类型。
分别创建以下方法:
- public void save(String id, T t):保存T类型的对象到Map成员变量中
- public T get(String id):从map中获取id对应的对象
- public void update(String id, T t): 替换map中key为指定id的内容,改为指定的t对象
- public List<T> list():返回map中存放的所有T对象
- 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;//略
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mrgr.cn/news/45799.html 如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!