文章目录
1、泛型
1.1、需求引出
package generic;
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class Generic01 {
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));
// 假如我们不小心, 添加了一只猫
arrayList.add(new Cat("招财猫", 8));
// 遍历
for (Object o : arrayList) {
// 向下转型 Object -> Dog
Dog dog = (Dog) o;
System.out.println(dog.getName() + " - " + dog.getAge());
/**
* 旺财 - 10
* 发财 - 1
* 小黄 - 5
* Exception in thread "main" java.lang.ClassCastException: generic.Cat cannot be cast to generic.Dog
* at generic.Generic01.main(Generic01.java:20)
* 由于里面混入了猫类, 在向下转型的时候报错了
*/
}
}
}
/**
* 请编写程序, 在 ArrayList 中, 添加 3 个 Dog 对象
* Dog 对象含有 name 和 age, 并输出 name 和 age (要求使用 getXxx())
*/
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;
}
}
1.2、使用传统方法的问题分析
- 不能对加入到集合 ArrayList中的数据类型进行约束(不安全)
- 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
1.3、用泛型来解决前面的问题
ArrayList arrayList = new ArrayList();
package generic;
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class Generic01 {
public static void main(String[] args) {
// 1. 当我们 ArrayList<Dog> 表示存放到 ArrayList 集合中的元素是 Dog 类型
// 2. 如果编译器发现添加的类型, 不满足要求, 就会报错
// 3. 在遍历的时候, 可以直接取出 Dog 类型而不是 Object
// 4. public class ArrayList<E> {} E 称为泛型, 那么 Dog->E
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)); // 使用了泛型以后, 该行报错
// 遍历
for (Dog dog : arrayList) { // 使用泛型以后, 这里不需要向下转型
System.out.println(dog.getName() + " - " + dog.getAge());
/**
* 旺财 - 10
* 发财 - 1
* 小黄 - 5
*/
}
}
}
/**
* 请编写程序, 在 ArrayList 中, 添加 3 个 Dog 对象
* Dog 对象含有 name 和 age, 并输出 name 和 age (要求使用 getXxx())
*/
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;
}
}
1.4、泛型的好处
1.5、泛型介绍
package generic;
public class Generic03 {
public static void main(String[] args) {
// 注意, 特别强调: E 具体的数据类型在定义 Person 对象的时候指定, 即在编译期间, 就确定 E 是什么类型
Person<String> person = new Person<String>("hello");
person.show(); // class java.lang.String
/**
* 可以这样理解上面的 Person 类
* class Person {
* String s; // E 表示 s 的数据类型, 该数据类型在定义 Person 对象的时候指定, 即在编译期间, 就确定 E 是什么类型
*
* public Person(String s) { // E 也可以是参数类型
* this.s = s;
* }
*
* public String f() { // 返回类型使用
* return s;
* }
*
* public void show() {
* System.out.println(s.getClass()); // 显示 s 的运行类型
* }
* }
*/
Person<Integer> person2 = new Person<>(100);
person2.show(); // class java.lang.Integer
/**
* class Person {
* Integer s; // E 表示 s 的数据类型, 该数据类型在定义 Person 对象的时候指定, 即在编译期间, 就确定 E 是什么类型
*
* public Person(Integer s) { // E 也可以是参数类型
* this.s = s;
* }
*
* public Integer f() { // 返回类型使用
* return s;
* }
*
* public void show() {
* System.out.println(s.getClass()); // 显示 s 的运行类型
* }
* }
*/
}
}
// 泛型的作用是: 可以在类声明时通过一个标识表示类中某个属性的类型
// 或者是某个方法的返回值的类型, 或者是参数类型
class Person<E> {
E s; // E 表示 s 的数据类型, 该数据类型在定义 Person 对象的时候指定, 即在编译期间, 就确定 E 是什么类型
public Person(E s) { // E 也可以是参数类型
this.s = s;
}
public E f() { // 返回类型使用
return s;
}
public void show() {
System.out.println(s.getClass()); // 显示 s 的运行类型
}
}
1.6、泛型的语法
1.6.1、泛型的声明
1.6.2、泛型的实例化
1.6.3、泛型使用举例
package generic;
import java.util.*;
public class GenericExercise {
public static void main(String[] args) {
// 使用泛型方式给 HashSet 放入 3 个学生对象
HashSet<Student> students = new HashSet<Student>();
students.add(new Student("jack", 18));
students.add(new Student("tom", 28));
students.add(new Student("mary", 19));
// 遍历
for (Student student : students) {
System.out.println(student);
/**
* Student{name='mary', age=19}
* Student{name='tom', age=28}
* Student{name='jack', age=18}
*/
}
// 使用泛型方式给 HashMap 放入 3 个学生对象
// K -> String V -> Student
HashMap<String, Student> hm = new HashMap<String, Student>();
/*
public class HashMap<K,V> {}
*/
hm.put("milan", new Student("milan", 38));
hm.put("smith", new Student("smith", 48));
hm.put("jack", new Student("jack", 15));
// 迭代器 EntrySet
/*
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
*/
Set<Map.Entry<String, Student>> entries = hm.entrySet();
/*
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
*/
Iterator<Map.Entry<String, Student>> iterator = entries.iterator();
System.out.println("==============================");
while (iterator.hasNext()) {
Map.Entry<String, Student> next = iterator.next();
System.out.println(next.getKey() + "-" + next.getValue());
}
/**
* ==============================
* smith-Student{name='smith', age=48}
* milan-Student{name='milan', age=38}
* jack-Student{name='jack', age=15}
*/
}
}
/**
* 创建 3 个学生对象
* 放入到 HashSet 中学生对象, 使用
* 放入到 HashMap 中, 要求 Key 是 String name, Value 就是 学生对象
* 使用两种方式遍历
*/
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 +
'}';
}
}
1.6.4、泛型使用的注意事项和细节
package generic;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"all"})
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());
aPig.f(); // class generic.A
Pig<A> aPig2 = new Pig<A>(new B());
aPig2.f(); // class generic.B
// 3.泛型的使用形式
ArrayList<Integer> list1 = new ArrayList<Integer>();
List<Integer> list2 = new ArrayList<Integer>();
// 在实际开发中, 我们往往简写
// 编译器会进行类型推断, 推荐使用下面写法
ArrayList<Integer> list3 = new ArrayList<>();
List<Integer> list4 = new ArrayList<>();
ArrayList<Pig> pigs = new ArrayList<>();
// 4.如果是这样写 泛型默认是 Object
ArrayList arrayList = new ArrayList(); // 等价 ArrayList<Object> arrayList = new ArrayList<Object>()
/**
* public boolean add(Object e) {
* ensureCapacityInternal(size + 1); // Increments modCount!!
* elementData[size++] = e;
* return true;
* }
*/
Tiger tiger = new Tiger();
/**
* class Tiger { // 类
* Object e;
*
* public Tiger() {
* }
*
* public Tiger(Object e) {
* this.e = e;
* }
* }
*/
}
}
class Tiger<E> { // 类
E e;
public Tiger() {
}
public Tiger(E e) {
this.e = e;
}
}
class A {
}
class B extends A {
}
class Pig<E> {
E e;
public Pig(E e) {
this.e = e;
}
public void f() {
System.out.println(e.getClass()); // 运行类型
}
}
1.6.5、泛型小练习
package generic;
import java.util.ArrayList;
import java.util.Comparator;
@SuppressWarnings({"all"})
public class GenericExercise02 {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("jack", 12000.95, new MyDate(1993, 10, 10)));
employees.add(new Employee("tom", 25000.40, new MyDate(1981, 11, 11)));
employees.add(new Employee("lucy", 5500.50, new MyDate(2004, 9, 12)));
// 循环遍历
for (Employee employee : employees) {
System.out.println(employee);
/**
* Employee{name='jack', sal=12000.95, birthday=MyDate{year=1993, month=10, day=10}}
* Employee{name='tom', sal=25000.4, birthday=MyDate{year=1981, month=11, day=11}}
* Employee{name='lucy', sal=5500.5, birthday=MyDate{year=2004, month=9, day=12}}
*/
}
// 排序
// 传入 Comparator 对象[使用泛型], 先按照 name 排序, 如果 name 相同, 则按生日日期的先后排序 【即: 定制排序】
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee employee1, Employee employee2) {
// 先对传入的参数进行验证
if (!(employee1 instanceof Employee && employee2 instanceof Employee)) {
System.out.println("类型不正确..");
return 0;
}
int i = employee1.getName().compareTo(employee2.getName());
if (i != 0) {
return i;
}
// 下面是对 birthday 的比较, 因此, 我们最好把这个比较, 放在 MyDate 类完成
// 封装后, 将来可维护性和复用性, 就大大增强
return employee1.getBirthday().compareTo(employee2.getBirthday());
}
});
// 排序后
System.out.println("=== 排序后 ===");
for (int j = 0; j < employees.size(); j++) {
System.out.println(employees.get(j));
/**
* === 排序后 ===
* Employee{name='jack', sal=12000.95, birthday=MyDate{year=1993, month=10, day=10}}
* Employee{name='lucy', sal=5500.5, birthday=MyDate{year=2004, month=9, day=12}}
* Employee{name='tom', sal=25000.4, birthday=MyDate{year=1981, month=11, day=11}}
*/
}
}
}
/**
* 定义 Employee 类
* 1) 该类包含: private 成员变量 name, sal, birthday, 其中 birthday 为 MyDate 类的对象
* 2) 为每一个属性定义 getter, setter 方法
* 3) 重写 toString 方法输出 name, sal, birthday
* 4) MyDate 类包含: private 成员变量 month, day, year, 并为每一个属性定义 getter, setter 方法
* 5) 创建该类的 3 个对象, 并把这些对象放入 ArrayList 集合中(ArrayList 需使用泛型来定义), 对集合中的元素进行排序, 并遍历输出
* 排序方式: 调用 ArrayList 的 sort 方法
* 传入 Comparator 对象[使用泛型], 先按照 name 排序, 如果 name 相同, 则按生日日期的先后排序 【即: 定制排序】
*/
// Employee 类
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 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;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
}
// MyDate 类
class MyDate implements Comparable<MyDate> {
private Integer year;
private Integer month;
private Integer day;
public MyDate(Integer year, Integer month, Integer day) {
this.year = year;
this.month = month;
this.day = day;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getMonth() {
return month;
}
public void setMonth(Integer month) {
this.month = month;
}
public Integer getDay() {
return day;
}
public void setDay(Integer day) {
this.day = day;
}
@Override
public String toString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
@Override
public int compareTo(MyDate o) { // 把对year-month-day比较放在这里
int yearMinus = year - o.getYear();
if (yearMinus != 0) {
return yearMinus;
}
// 如果year相同, 就比较month
int monthMinus = month - o.getMonth();
if (monthMinus != 0) {
return monthMinus;
}
// 如果year 和 month 相同直接返回day的比较结果
return day - o.getDay();
}
}