一:泛型引入:
1.1 使用传统方法的问题分析:
1) 不能对加入到集合 ArrayList 中的数据类型进行约束。
2) 遍历的时候,需要进行类型转换,如果集合中的数据量比较大,对效率有影响。
1.2 泛型快速体验:
package com.hspedu.generic.improve;
import java.util.ArrayList;
@SuppressWarnings({"all"})
/*
1.请编写程序,在ArrayList 中,添加3个Dog对象
2.Dog对象含有name 和 age, 并输出name 和 age (要求使用getXxx())
3.使用泛型来完成代码
*/
public class Generic02 {
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));
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;
}
//省略getter、setter方法
}
class Cat { //Cat类
private String name;
private int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
//省略getter、setter方法
}
1.3 泛型的好处:
1) 编译时,检查添加元素的类型,提高了安全性。
2) 减少了类型转换的次数,提高了效率。
当不使用泛型:
Dog -> Object -> Dog
使用泛型
Dog -> Dog -> Dog
3) 不在提示编译警告。
1.4 泛型的介绍:
泛型可以理解为表示数据类型的一种数据类型
1)泛型又称参数化类型,是JDk 5.0 出现的新特性,解决数据类型的安全性问题。
2)在类声明或实例化时只要指定好需要的具体的类型即可。
3)Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException 异常。同时: 代码更加简洁,健壮。
4)泛型的作用是: 可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
package com.hspedu.generic;
import java.util.List;
public class Generic03 {
public static void main(String[] args) {
//注意,特别强调: E具体的数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型
Person<String> person = new Person<String>("tom");
person.show(); //String
/*
你可以这样理解,上面的Person类
class Person {
String s ;//E表示 s的数据类型, 该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么类型
public Person(String s) {//E也可以是参数类型
this.s = s;
}
public String f() {//返回类型使用E
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 show() {
System.out.println(s.getClass());//显示s的运行类型
}
}
1.5 泛型的语法:
interface<k>{ } 和 class <k,v>{ }, 比如 List 和 ArrayList
1)其中,T,K,V不代表值,而是代表类型。
2)任何字母都可以。常用 T 表示,是 Type 的缩写。
泛型的实例化,要在类名后面指定类型参数的值(类型),比如
1) List<String> strList = new ArrayList<String>( ) ;
2) Lterator<Customer> iterator = customers.iterator( );
1.6 泛型的使用实例演示:
要求创建三个学生对象,分别放到 HashSet 和 HashMap 中进行遍历。
package com.hspedu.generic;
import java.util.*;
@SuppressWarnings({"all"})
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);
}
//使用泛型方式给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("hsp", new Student("hsp", 28));
//迭代器 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());
}
}
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//省略getter、setter、toString方法
}
二 :泛型的细节:
2.1 泛型使用的注意事项和细节:
1)interface List<T> { } 和,public class HashSet<E> { } 等等... T,E 只能是引用类型。
ArrayList<Integer> integers = new ArrayList<>(); //ok
ArrayList<int> ints = new ArrayList<int>(); //false
2) 在指定泛型具体类型后,可以传入该类型或者其子类类型。
//因为 E 指定了 A 类型, 构造器传入了 new A()
//在给泛型指定具体类型后,可以传入该类型或者其子类类型
Pig<A> aPig = new Pig<A>(new A());
aPig.f();
Pig<A> aPig2 = new Pig<A>(new B());
aPig2.f();
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()); //运行类型
}
}
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<>();
注意: 如果这样写 :List list = new ArrayList( ); 默认于给他的泛型<E> ,E 是 Object。
2.2 泛型细节练习:
package com.hspedu.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("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)));
System.out.println("employees=" + employees);
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee emp1, Employee emp2) {
//先按照name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】
//先对传入的参数进行验证
if(!(emp1 instanceof Employee && emp2 instanceof Employee)) {
System.out.println("类型不正确..");
return 0;
}
//比较name
int i = emp1.getName().compareTo(emp2.getName());
if(i != 0) {
return i;
}
//下面是对birthday的比较,因此,我们最好把这个比较,放在MyDate类完成
//封装后,将来可维护性和复用性,就大大增强.
return emp1.getBirthday().compareTo(emp2.getBirthday());
}
});
System.out.println("==对雇员进行排序==");
System.out.println(employees);
}
}
public class MyDate implements Comparable<MyDate>{
private int year;
private int month;
private int day;
//省略构造器、getter、setter、toString方法
@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
return day - o.getDay();
}
}