1.泛型的理解和好处
看一个需求:
- 请编写程序,在ArrayList中添加三个Dog对象
- Dog对象含有name和age,并输出name和age(要求使用getXXX())
先用传统的方法来解决--->引出泛型
传统的方法:
package li.generic; | |
import java.util.ArrayList; | |
@SuppressWarnings("all") | |
public class Introduce_ { | |
public static void main(String[] args) { | |
//用传统的方法来解决 | |
ArrayList arrayList = new ArrayList(); | |
arrayList.add(new Dog("旺财",10)); | |
arrayList.add(new Dog("发财",1)); | |
arrayList.add(new Dog("小黄",5)); | |
for (Object o:arrayList) { | |
//向下转型 | |
Dog dog = (Dog) o; | |
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; | |
} | |
} |
//假设,我们的程序员不小心添加了一只猫 | |
arrayList.add(new Cat("招财猫",8)); |
那么 在使用增强for循环输出的时候向下转型时就会抛出异常:类型转换错误
使用传统方法问题的分析:
- 不能对加入到集合ArrayList中的数据进行约束(不安全)
- 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
使用泛型来解决问题:
package li.generic; | |
import java.util.ArrayList; | |
@SuppressWarnings("all") | |
public class Introduce_ { | |
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)); | |
System.out.println("====使用泛型===="); | |
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; | |
} | |
} |
package li.generic; | |
import java.util.ArrayList; | |
@SuppressWarnings("all") | |
public class Introduce_ { | |
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)); | |
System.out.println("====使用泛型===="); | |
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; | |
} | |
} |
泛型的好处:
-
编译时,检查添加元素的类型,提高了安全型
-
减少了类型转换的次数,提高效率
如上面例子所示:不使用泛型的时候,Dog对象放到ArrayList里会先转成Object类型,在取出的时候还要再转换成Dog类型(Dog--加入-->Object--取出-->Dog)
使用了泛型,则放入和取出时都不需要类型转换,提高效率(Dog-->-Dog-->Dog)
-
不再提示编译警告
不添加
@SuppressWarnings("all")
编译器也不再警告
2.泛型介绍
泛型是一种可以表示数据类型的 数据类型
如下图:public class ArrayList<E>{}
E 称为泛型
泛(广泛)型(类型)===>integer,String,Dog,……
- 泛型又称参数化类型,是jdk5.0出现的新特性,解决数据类型的安全性问题
- 在类声明或者实例化时只要指定好需要的具体类型即可
- Java泛型可以保证如果程序在编译时没有发出警告,运行就不会产生ClassCastException异常。同时,代码更加简洁、健壮
- 泛型的作用是:可以在类声明时 通过一个标识 表示类中的某个属性,或者是某个方法的返回值的类型,或者是参数类型
例子:
package li.generic; | |
public class Generic03 { | |
public static void main(String[] args) { | |
Person<String> person = new Person<String>("jack"); | |
person.showCalss();//class java.lang.String | |
/* | |
可以这样理解:上面的Person类变为了 | |
class Person{ | |
String s; | |
public Person(String s) { | |
this.s = s; | |
} | |
public String f() { | |
return s; | |
} | |
} | |
*/ | |
Person<Integer> person1 = new Person<Integer>(100); | |
person1.showCalss();//class java.lang.Integer | |
/* 可以这样理解:上面的Person类变为了 | |
class Person{ | |
Integer s; | |
public Person(Integer s) { | |
this.s = s; | |
} | |
public Integer f() { | |
return s; | |
} | |
} | |
*/ | |
} | |
} | |
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 showCalss(){ | |
System.out.println(s.getClass());//显示s的运行类型 | |
} | |
} |
注意:E的数据类型在定义 Person 对象的时候指定,即在编译期间,就确定E是什么类型
泛型是一种可以表示数据类型的 数据类型
3.泛型的语法
3.1泛型的声明
interface 接口<T>{}
和class 类<K,V>{}//比如:List、ArrayList
说明:
1)其中,T,K,V不代表值,而是表示类型
2)任意字母都可以。常用T表示,是Type的缩写
3.2泛型的实例化
要在类名后面指定类型参数的值(类型),如:
(1)List<String> strList = new ArrayList<String>() ;
(2)Iterator<Customer> iterator = customer.iterator();
3.3泛型使用举例
例子:泛型使用举例:
练习:
- 创建三个学生对象
- 学生对象放入到HashSet中使用
- 放入到HashMap中,要求Key是String name ,Value就是学生对象
- 使用两种方法遍历
练习:
package li.generic; | |
import java.util.*; | |
public class GenericExercise { | |
public static void main(String[] args) { | |
//使用泛型的方法给HashSet放入三个学生对象 | |
HashSet<Student> students = new HashSet<Student>(); | |
students.add(new Student("jack", 18)); | |
students.add(new Student("marry", 17)); | |
students.add(new Student("link", 123)); | |
//使用HashSet的增强for | |
System.out.println("===使用HashSet的增强for==="); | |
for (Student student : students) { | |
System.out.println(student); | |
} | |
//使用泛型的方法给HashMap放入三个学生对象 | |
HashMap<String, Student> hm = new HashMap<String, Student>(); | |
hm.put("jack", new Student("jack", 18)); | |
hm.put("lucy", new Student("lucy", 28)); | |
hm.put("olin", new Student("olin", 16)); | |
//迭代器 EntrySet | |
System.out.println("===迭代器 EntrySet==="); | |
Set<Map.Entry<String,Student>> entries = hm.entrySet(); | |
Iterator<Map.Entry<String,Student>> iterator1 = entries.iterator(); | |
while (iterator1.hasNext()) { | |
Map.Entry<String, Student> next = iterator1.next(); | |
System.out.println(next.getKey()+"-"+next.getValue()); | |
} | |
} | |
} | |
class Student { | |
private String name; | |
private int age; | |
public Student(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; | |
} | |
@Override | |
public String toString() { | |
return "Student{" + | |
"name='" + name + '\'' + | |
", age=" + age + | |
'}'; | |
} | |
} |
3.4泛型使用的注意事项和细节
-
interface List<T>{} ,public class HashSet<E>{}..等等
说明:T,E只能是引用类型
看看下面语句是否正确?
List<Integer> list = new ArrayList<Integer>();//正确
List<int> list2 = new ArrayList<int>();//错误
-
在指定泛型具体类型后,可以传入该类型或者其子类类型
-
泛型使用形式
3.1 在实际的开发中,我们往往简写,编译器会进行类型推断,推荐使用下面的写法
ArrayList<Integer> list1 = new ArrayList<>();
3.2 泛型默认是Object类型,即如果没有给泛型指定类型,默认就是Object:
ArrayList arrayList = new ArrayList();//等价为 ArrayList<Object> arrayList = new ArrayList<>();
例子:
package li.generic; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class GenericDetail { | |
public static void main(String[] args) { | |
//1.给泛型指向的数据类型要求是引用类型,不能是基本数据类型 | |
List<Integer> list = new ArrayList<Integer>();//ok | |
//List<int> list2 = new ArrayList<int>();错误 | |
//2.因为 E指定了A类型,构造器传入了 new A() | |
Pig<A> aPig = new Pig<A>(new A());//将A类型赋给泛型E,说明Pig构造器可以接收的是A类型的对象 | |
//在指定泛型具体类型后,可以传入该类型或者其子类类型 | |
Pig<A> aPig2 = new Pig<A>(new B()); | |
aPig.showClass();//class li.generic.A | |
aPig2.showClass();//class li.generic.B | |
// 3.泛型的使用形式 | |
//在实际的开发中,我们往往简写,编译器会进行类型推断,推荐使用下面的写法 | |
ArrayList<Integer> list1 = new ArrayList<>(); | |
} | |
} | |
class A{} | |
class B extends A{} | |
class Pig<E>{ | |
E e; | |
public Pig(E e) { | |
this.e = e; | |
} | |
public void showClass(){ | |
System.out.println(e.getClass());//运行类型 | |
} | |
} |
4.泛型课堂练习
定义Employee类
-
该类包括:private成员变量name,sal,birthday,其中birthday为MyDate类的对象;
-
为每一个属性定义getter、setter方法;
-
重写toString方法输出name,sal,birthday;
-
MyDate类包括:private成员变量year,month,day。并为为每一个属性定义getter、setter方法;
-
创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList需使用泛型来定义),对集合中的元素进行排序,并遍历输出:
排序方式:调用ArrayList的sort方法,传入Comparator对象(使用泛型),先按照name排序,如果name相同,则按照生日日期的先后排序。(即定制排序)
练习:
package li.generic; | |
import java.util.ArrayList; | |
import java.util.Comparator; | |
public class GenericHomework { | |
public static void main(String[] args) { | |
ArrayList<Employee> employees = new ArrayList<>(); | |
employees.add(new Employee("tom", 20000, new MyDate(1980, 12, 11))); | |
employees.add(new Employee("jack", 12000, new MyDate(2001, 12, 12))); | |
employees.add(new Employee("tom", 50000, new MyDate(1980, 12, 10))); | |
employees.sort(new Comparator<Employee>() { | |
@Override | |
public int compare(Employee o1, Employee o2) { | |
// //比较name | |
// int i = o1.getName().compareTo(o2.getName()); | |
// if (i != 0) { | |
// return i; | |
// } | |
// //如果name相同,就比较birthday-year | |
// int yearMinus = o1.getBirthday().getYear()-o2.getBirthday().getYear(); | |
// if (yearMinus !=0) { | |
// return yearMinus; | |
// } | |
// //如果year相同,就比较month | |
// int monthMinus = o1.getBirthday().getMonth()-o2.getBirthday().getMonth(); | |
// if (monthMinus !=0) { | |
// return monthMinus; | |
// } | |
// //如果month相同,就比较mday | |
// return o1.getBirthday().getDay()-o2.getBirthday().getDay(); | |
//比较name | |
int i = o1.getName().compareTo(o2.getName()); | |
if (i != 0) { | |
return i; | |
} | |
//下面是对birthday的比较,因此,我们最好把日期的比较放到MyDate类完成 | |
//封装后的维护性和复用性更好 | |
return o1.getBirthday().compareTo(o2.getBirthday()); | |
} | |
}); | |
for (Employee e : employees) { | |
System.out.println(e); | |
} | |
} | |
} | |
class Employee { | |
private String name; | |
private int sal; | |
private MyDate birthday; | |
public Employee(String name, int sal, MyDate birthday) { | |
this.name = name; | |
this.sal = sal; | |
this.birthday = birthday; | |
} | |
public String getName() { | |
return name; | |
} | |
public void setName(String name) { | |
this.name = name; | |
} | |
public int getSal() { | |
return sal; | |
} | |
public void setSal(int sal) { | |
this.sal = sal; | |
} | |
public MyDate getBirthday() { | |
return birthday; | |
} | |
public void setBirthday(MyDate birthday) { | |
this.birthday = birthday; | |
} | |
@Override | |
public String toString() { | |
return "Employee{" + | |
"name='" + name + '\'' + | |
", sal=" + sal + | |
", birthday=" + birthday + | |
'}'; | |
} | |
} | |
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 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 "MyDate{" + | |
"year='" + year + '\'' + | |
", month='" + month + '\'' + | |
", day='" + day + '\'' + | |
'}'; | |
} | |
@Override | |
public int compareTo(MyDate o) {//把 年 月 日 的比较挪到这里 | |
//如果name相同,就比较birthday-year | |
int yearMinus = year-o.getYear(); | |
if (yearMinus !=0) { | |
return yearMinus; | |
} | |
//如果year相同,就比较month | |
int monthMinus = month-o.getMonth(); | |
if (monthMinus !=0) { | |
return monthMinus; | |
} | |
//如果month相同,就比较mday | |
return day-o.getDay(); | |
} | |
} |