01-泛型在集合、比较器中的使用
1. 什么是泛型?
所谓泛型,就是允许在定义类、接口时通过一个`标识`表示类中某个`属性的类型`或者是某个方法的
`返回值或参数的类型`。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)
确定(即传入实际的类型参数,也称为类型实参)。
2. 在集合中使用泛型之前可能存在的问题
问题1:类型不安全。因为add()的参数是Object类型,意味着任何类型的对象都可以添加成功
问题2:需要使用强转操作,繁琐。还有可能导致ClassCastException异常。
3. 在集合、比较器中使用泛型 (重点)
见代码。
题目1:
题目需求 1. 定义一个Employee类。 该类包含:private成员变量name,age,birthday,其中 birthday 为 MyDate 类的对象; 并为每一个属性定义 getter, setter 方法; 并重写 toString 方法输出 name, age, birthday 2. MyDate类包含: private成员变量year,month,day;并为每一个属性定义 getter, setter 方法; 3. 创建该类的 5 个对象,并把这些对象放入 TreeSet 集合中(TreeSet 需使用泛型来定义) 4. 分别按以下两种方式对集合中的元素进行排序,并遍历输出: 1). 使Employee 实现 Comparable 接口,并按 name 排序 2). 创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序。 package com.atguigu01.use.exer1; /** * ClassName: Employee * Description: * 定义一个Employee类。 * 该类包含:private成员变量name,age,birthday,其中 birthday 为 MyDate 类的对象; * 并为每一个属性定义 getter, setter 方法; * 并重写 toString 方法输出 name, age, birthday * @Author 尚硅谷-宋红康 * @Create 17:03 * @Version 1.0 */ public class Employee implements Comparable<Employee>{ private String name; private int age; private MyDate birthday; public Employee() { } public Employee(String name, int age, MyDate birthday) { this.name = name; this.age = age; this.birthday = birthday; } 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; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", age=" + age + ", birthday=[" + birthday + "]}"; } //按照name从低到高排序 @Override public int compareTo(Employee o) { return this.name.compareTo(o.name); } }题目需求
package com.atguigu01.use.exer1; /** * ClassName: MyDate * Description: * MyDate类包含: * private成员变量year,month,day;并为每一个属性定义 getter, setter 方法; * * @Author 尚硅谷-宋红康 * @Create 17:04 * @Version 1.0 */ public class MyDate implements Comparable<MyDate>{ private int year; private int month; private int day; public MyDate() { } 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 year + "年" + month + "月" + day + "日"; } //按照生日从小到大排列 @Override public int compareTo(MyDate o) { int yearDistince = this.getYear() - o.getYear(); if(yearDistince != 0){ return yearDistince; } int monthDistince = this.getMonth() - o.getMonth(); if(monthDistince != 0){ return monthDistince; } return this.getDay() - o.getDay(); } }MyDate
package com.atguigu01.use.exer1; import org.junit.Test; import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; /** * ClassName: EmployeeTest * Description: * * @Author 尚硅谷-宋红康 * @Create 17:06 * @Version 1.0 */ public class EmployeeTest { //需求1:使Employee 实现 Comparable 接口,并按 name 排序 @Test public void test1(){ TreeSet<Employee> set = new TreeSet<>(); Employee e1 = new Employee("HanMeimei",18,new MyDate(1998,12,21)); Employee e2 = new Employee("LiLei",20,new MyDate(1996,11,21)); Employee e3 = new Employee("LiHua",21,new MyDate(2000,9,12)); Employee e4 = new Employee("ZhangLei",19,new MyDate(1997,5,31)); Employee e5 = new Employee("ZhangYi",23,new MyDate(2001,11,2)); set.add(e1); set.add(e2); set.add(e3); set.add(e4); set.add(e5); //遍历 Iterator<Employee> iterator = set.iterator(); while(iterator.hasNext()){ Employee employee = iterator.next(); System.out.println(employee); } } //需求2:创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序。 @Test public void test2(){ Comparator<Employee> comparator = new Comparator<Employee>(){ @Override public int compare(Employee o1, Employee o2) { //错误的写法: // return o1.getBirthday().toString().compareTo(o2.getBirthday().toString()); //正确的写法1: // int yearDistince = o1.getBirthday().getYear() - o2.getBirthday().getYear(); // if(yearDistince != 0){ // return yearDistince; // } // // int monthDistince = o1.getBirthday().getMonth() - o2.getBirthday().getMonth(); // if(monthDistince != 0){ // return monthDistince; // } // // return o1.getBirthday().getDay() - o2.getBirthday().getDay(); //正确的写法2: return o1.getBirthday().compareTo(o2.getBirthday()); } }; TreeSet<Employee> set = new TreeSet<>(comparator); Employee e1 = new Employee("HanMeimei",18,new MyDate(1998,12,21)); Employee e2 = new Employee("LiLei",20,new MyDate(1996,11,21)); Employee e3 = new Employee("LiHua",21,new MyDate(2000,9,12)); Employee e4 = new Employee("ZhangLei",19,new MyDate(1996,5,31)); Employee e5 = new Employee("ZhangYi",23,new MyDate(2000,9,2)); set.add(e1); set.add(e2); set.add(e3); set.add(e4); set.add(e5); //遍历 Iterator<Employee> iterator = set.iterator(); while(iterator.hasNext()){ Employee employee = iterator.next(); System.out.println(employee); } } }测试代码
题目2:
(1)创建一个ArrayList集合对象,并指定泛型为<Integer> (2)添加5个[0,100)以内的随机整数到集合中 (3)使用foreach遍历输出5个整数 (4)使用集合的removeIf方法删除偶数,为Predicate接口指定泛型<Ineteger> (5)再使用Iterator迭代器输出剩下的元素,为Iterator接口指定泛型题目需求
package com.atguigu01.use.exer2; import java.util.ArrayList; import java.util.Iterator; import java.util.function.Predicate; /** * ClassName: Exer02 * Description: * * @Author 尚硅谷-宋红康 * @Create 17:23 * @Version 1.0 */ public class Exer02 { public static void main(String[] args) { //(1)创建一个ArrayList集合对象,并指定泛型为<Integer> ArrayList<Integer> list = new ArrayList<>(); //(2)添加5个[0,100)以内的随机整数到集合中 for (int i = 0; i < 5; i++) { int random = (int)(Math.random() * (99 - 0 + 1)); list.add(random); } //(3)使用foreach遍历输出5个整数 for(Integer value : list){ System.out.println(value); } //(4)使用集合的removeIf方法删除偶数,为Predicate接口指定泛型<Ineteger> list.removeIf(new Predicate<Integer>(){ @Override public boolean test(Integer value) { return value % 2 == 0; } }); System.out.println(); //(5)再使用Iterator迭代器输出剩下的元素,为Iterator接口指定泛型<Integer> Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ Integer value = iterator.next(); System.out.println(value); } } }代码
package com.atguigu01.use; import org.junit.Test; import java.util.*; /** * @author 尚硅谷-宋红康 * @create 11:39 */ public class CollectionMapTest { //体会集合中使用泛型前的场景 @Test public void test1(){ List list = new ArrayList(); list.add(67); list.add(78); list.add(76); list.add(99); //1.问题1:类型不安全。因为add()的参数是Object类型,意味着任何类型的对象都可以添加成功 // list.add("AA"); Iterator iterator = list.iterator(); while(iterator.hasNext()){ //2.问题2:需要使用强转操作,繁琐。还有可能导致ClassCastException异常。 Integer i = (Integer) iterator.next(); int score = i; System.out.println(score); } } //在集合中使用泛型的例子 @Test public void test2(){ List<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(76); list.add(66); list.add(99); //编译报错,保证类型的安全 // list.add("AA"); Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ //因为添加的都是Integer类型,避免了强转操作 Integer i = iterator.next(); int score = i; System.out.println(score); } } /* * 泛型在Map中使用的例子 * */ @Test public void test3(){ // HashMap<String,Integer> map = new HashMap<String,Integer>(); //jdk7的新特性 HashMap<String,Integer> map = new HashMap<>(); //类型推断 map.put("Tom",67); map.put("Jerry",87); map.put("Rose",99); // Set<Map.Entry<String,Integer>> entrySet = map.entrySet(); // Iterator<Map.Entry<String, Integer>> iterator = entrySet.iterator(); var entrySet = map.entrySet(); var iterator = entrySet.iterator(); while(iterator.hasNext()){ Map.Entry<String, Integer> entry = iterator.next(); String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key + "--->" + value); } } }测试代码
4.使用说明
> 集合框架在声明接口和其实现类时,使用了泛型(jdk5.0),在实例化集合对象时,
如果没有使用泛型,则认为操作的是Object类型的数据。
如果使用了泛型,则需要指明泛型的具体类型。一旦指明了泛型的具体类型,则在集合的相关的方法中,凡是使用类的泛型的位置,都替换为具体的泛型类型。
02-自定义泛型
1. 自定义泛型类\接口
1.1 格式
class A<T>{
}
interface B<T1,T2>{
}
1.2 使用说明
① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。
② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
- 经验:泛型要使用一路都用。要不用,一路都不要用。
④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤
除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。比如:SubOrder2
如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。比如:SubOrder3
我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。比如:比如:SubOrder4,SubOrder5
1.3 注意点
① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
② JDK7.0 开始,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();
③ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
④ 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
⑤ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
⑥ 异常类不能是带泛型的。
2. 自定义泛型方法
2.1 问题:在泛型类的方法中,使用了类的泛型参数。那么此方法是泛型方法吗?
2.2 格式
权限修饰符 <T> 返回值类型 方法名(形参列表){ //通常在形参列表或返回值类型的位置会出现泛型参数T
}
2.3 举例
public <E> E method(E e){
}
2.4 说明
> 声明泛型方法时,一定要添加泛型参数<T>
> 泛型参数在方法调用时,指明其具体的类型
> 泛型方法可以根据需要声明为static的
> 泛型方法所属的类是否是一个泛型类,都可以。
03-泛型在继承上的体现
1. 类SuperA是类A的父类,则G<SuperA> 与 G<A>的关系:G<SuperA> 和 G<A>是并列的两个类,没有任何子父类的关系。
比如:ArrayList<Object> 、ArrayList<String>没有关系
2. 类SuperA是类A的父类或接口,SuperA<G> 与 A<G>的关系:SuperA<G> 与A<G> 有继承或实现的关系。
即A<G>的实例可以赋值给SuperA<G>类型的引用(或变量)
比如:List<String> 与 ArrayList<String>
04-通配符的使用
1. 通配符: ?
2. 使用说明:
> 举例:ArrayList<?>
> G<?> 可以看做是G<A>类型的父类,即可以将G<A>的对象赋值给G<?>类型的引用(或变量)
3. 读写数据的特点(以集合ArrayList<?>为例说明)
> 读取数据:允许的,读取的值的类型为Object类型
> 写入数据:不允许的。特例:写入null值。
4. 有限制条件的通配符
List<? extends A> : 可以将List<A>或List<B>赋值给List<? extends A>。其中B类是A类的子类。
List <? super A> :可以将List<A>或List<B>赋值给List<? extends A>。其中B类是A类的父类。
5. 有限制条件的统配符的读写操作(难、了解)
技巧:开发中,遇到了带限制条件的通配符,在赋值时,如果没报错,那就正常使用。
如果报错了,知道不能这样写。改改!
标签:iterator,int,Employee,泛型,new,public From: https://www.cnblogs.com/callbin/p/17958717