JAVA 泛型
泛型
一、泛型引入
package com.hspedu.generic;
import java.util.ArrayList;
/**
* @author DL5O
* @version 1.0
*/
@SuppressWarnings("all")
public class Generic01 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new Dog("旺财",5));
list.add(new Dog("发财",1));
list.add(new Dog("小黄",6));
list.add(new Cat("招财猫",8));//加入一只猫
//遍历
for (Object o :list) {
Dog dog = (Dog)o;//向下转型
System.out.println("name=" + dog.getName() + ",age=" + 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 int getAge() {
return 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 int getAge() {
return age;
}
}
使用传统方法的问题分析
- 不能对加入到集合ArrayList中的数据类型进行约束(不安全)
- 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
二、泛型入门
package com.hspedu.generic.improve;
import java.util.ArrayList;
/**
* @author DL5O
* @version 1.0
*/
@SuppressWarnings("all")
public class Generic02 {
public static void main(String[] args) {
//引入泛型
//传统方法解决 ===> 使用泛型
//1.当我们 ArrayList<Dog> 表示存放到ArrayList 集合中的元素是Dog类型
//2.如果编译器发现添加的类型,不满足要求,就会报错
//3.在遍历的时候,可以直接取出 Dog 类型,而不是Object类型
ArrayList<Dog> list = new ArrayList<Dog>();
list.add(new Dog("旺财",5));
list.add(new Dog("发财",1));
list.add(new Dog("小黄",6));
// list.add(new Cat("招财猫",8));//加入一只猫,使用泛型后,就加入不了了
System.out.println("===使用泛型===");
for (Dog dog :list) {
System.out.println("name=" + dog.getName() + ",age=" + 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 int getAge() {
return 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 int getAge() {
return age;
}
}
- 当我们 ArrayList 表示存放到ArrayList 集合中的元素是Dog类型
- 如果编译器发现添加的类型,不满足要求,就会报错
- 在遍历的时候,可以直接取出 Dog 类型,而不是Object类型
小结:
- 编译时,检查添加元素的类型,提高了安全性
- 减少了类型转换的次数,提高效率
- 不再提示编译警告
不使用泛型
Dog -加入-> Object -取出 -> Dog//放到ArrayList中会先转成Object,在取出时,还需要转成Dog
使用泛型
Dog -> Dog -> Dog//放入和取出时,不需要进行类型的转换,提高效率
三、泛型介绍
老韩理解:泛(广泛)型(类型)=> Integer, String,Dog
- 即可以表示其他数据的类型的数据类型
-
泛型又称参数化类型,是Jdk5.0出现的新特性,解决数据类型的安全性问题
-
在类声明或实例化时只要指定好需要的具体的类型即可。
-
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生,
ClassCastException
异常。同时,代码更加简洁、健壮 -
泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。[有点难,举例Generic03.java]
注意:(特别强调!!!)
- E的数据类型在定义的Person的时候指定,即在编译期间,就确定E是什么类型
package com.hspedu.generic;
/**
* @author DL5O
* @version 1.0
*/
public class Generic03 {
public static void main(String[] args) {
Person<String> ywl = new Person<>("ywl");
ywl.show();//String
/*
可以这样理解,上面的Person类
把E 替换成了String,可以这样理解,但这样不太准确
*/
Person<Integer> integerPerson = new Person<>(100);
//E 这个泛型在定义的时候就知道了
integerPerson.show();//Interger
}
}
//泛型的作用:可以在类声明时通过一个标识表示类中某个属性的类型,
// 或者是某个方法的返回值的类型,或者是参数类型。
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的运行类型
}
}
四、泛型的语法
interface 接口{} 和 class <K,V>{}
比如:List,ArrayList
说明:
- 其中,T,K,V不代表值,而是表示类型
- 任意字母都可以。 常用T表示(Type)
泛型的实例化
- List strList = new ArrayList();
- Iterator iterator = customers.iterator();
package com.hspedu.generic;
import java.util.*;
/**
* @author DL5O
* @version 1.0
*/
public class GenericExercise {
public static void main(String[] args) {
Student ywl = new Student("ywl", 21);
Student zsj = new Student("zsj", 20);
Student sky = new Student("sky", 19);
//HashMap 遍历方式
// System.out.println("====HashMap遍历方式====");
HashMap<String, Student> map = new HashMap<String, Student>();
/*
public class HashMap<K,V>
*/
map.put("ywl", ywl);
map.put("zsj", zsj);
map.put("sky", sky);
System.out.println("====keySet====");
//迭代器 EntrySet
Set<Map.Entry<String, Student>> entries = map.entrySet();
/*
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
*/
Iterator<Map.Entry<String, Student>> iterator1 = entries.iterator();
while (iterator1.hasNext()) {
Map.Entry<String, Student> student = iterator1.next();
System.out.println(student.getKey() + "-" + student.getValue());
}
//HashSet
System.out.println("====HashSet====");
HashSet<Student> students = new HashSet<>();
students.add(ywl);
students.add(zsj);
students.add(sky);
//迭代器遍历
Iterator<Student> iterator = students.iterator();
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student);
}
System.out.println("====增强for循环====");
//for循环遍历
for (Student student : students) {
System.out.println(student);
}
}
}
class Student {
public String name;
public int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "姓名:" + name + ",年龄:" + age;
}
}
注意:
- 定义
HashMap
的时候,已经把key和value指定了,而迭代器中的k,v也能跟着确定下来,故存进去的都是<String,Student> 的键值对,取出来的也是这样的一对键值对- 然后使用getValue方法可以获得Student对象,getKey可以获得键值
- K-> String,Value-> Student;
五、泛型语法和使用
-
interface List{}, public class HashSet{}..等等
- 这里的E,T…等等只能是引用类型
- 即给泛型指定类型的时候,只能是引用类型,不能是基本数据类型
List<Integer> list = new ArrayList<Integer>();//OK List<int> list = new ArrayList<int>();//只能是引用类型
-
在给泛型指定了的具体类型后,可以传入该类型的子类型或者改类型
-
泛型使用形式
List<Interger> list = new ArrayList<>();//推荐写法 List<Interger> list = new ArrayList<Interger>();
- 如果这样写
List list = new ArrayList();
他的默认泛型是Object;- 它等价于:
List<Object> list = new ArrayList<>();
- 它等价于:
- 如果这样写
课堂练习题
package com.hspedu.generic.exercise;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
/**
* @author DL5O
* @version 1.0
*/
public class GenericExercise01 {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("小龙23", 5000, new MyDate(2000, 9, 9)));
employees.add(new Employee("大龙23", 6000, new MyDate(2001, 10, 9)));
employees.add(new Employee("小张", 7500, new MyDate(2002, 11, 9)));
employees.add(new Employee("小宋", 6500, new MyDate(2001, 5, 9)));
System.out.println("====排序前====");
for (Employee e : employees) {
System.out.println(e);
System.out.println();
}
System.out.println("====进行排序====");
//进行排序操作
//按照name进行排序,如果name相同,就按照生日的先后顺序进行排序
Collections.sort(employees, new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
// int ret = e1.getName().compareTo(e2.getName());
int ret = e1.getName().length() - e2.getName().length();
//长度也按从小到大来比较
if (ret != 0) {
return ret;
}
//如果名字/名字长度相同
//出生年月按从大到下进行比较
//下面是对birthday的比较,因此,我们最好把这个比较,放在MyDate类完成
//封装后,将来可维护性和复用性,就大大增强
MyDate e1Birthday = e1.getBirthday();
MyDate e2Birthday = e2.getBirthday();
return e1Birthday.compareTo(e2Birthday);
}
});
Iterator<Employee> iterator = employees.iterator();
while (iterator.hasNext()) {
Employee e = iterator.next();
System.out.println(e);
System.out.println();
}
}
/*//对ArrayList 集合中的元素进行排序
public static void sort(ArrayList<Employee> employees){
}*/
}
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 "姓名:" + name + "\n工资:" + sal + "\n出生日期:" + 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 year + "." + month + "." + day;
}
@Override
public int compareTo(MyDate o) {//把对年月日year-month-day的比较放在这里
/*if (year > o.getYear()) {
return 1;
} else if (month > o.getMonth()) {
return 1;
} else if (day > o.getDay()) {
return 1;
} else if (year < o.getYear()
|| month < o.getMonth() ||
day < o.getDay()) {
return -1;
} else {
return 0;
}*/
//老师的思路
//如果出生年份不一样
int yearMinus = year - o.getYear();
if (yearMinus != 0) {
return yearMinus;
}
//如果出生年份一样,但月份不一样
int monthMinus = month - o.getMonth();
if (monthMinus != 0) {
return monthMinus;
}
//如果出生年份月份一样,就比较日期
int dayMinus = day - o.getDay();
return dayMinus;
}
}
六、自定义泛型
自定义泛型类
基本语法:
class 类名<T,R...>{//可以有多个
成员
}
注意细节:
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认为
Object
应用案例:
package com.hspedu.generic.cusotmgeneric;
/**
* @author DL5O
* @version 1.0
*/
public class CustomGeneric {
public static void main(String[] args) {
}
}
//解读
//1.Tiger后面有泛型,所有我们把Tiger就称为 自定义泛型类
//2.T,R,M泛型标识符,一般那都是字母大写
//3.泛型可以有多个
//4.普通成员,属性,方法都可以使用泛型
class Tiger<T,R,M>{
String name;
R r; //属性
M m;
T t;
//因为数组在new 的时候,不能确定T的类型,就无法在内存中开空间
T [] ts;//使用泛型的不允许直接初始化
public Tiger(String name){
this.name = name;
}
public Tiger(String name, R r, M m, T t) {
this.name = name;
this.r = r;
this.m = m;
this.t = t;
}
//在加载tiger类的时候,M泛型仍然不确定是怎么样的类型
//因为是静态的和类相关的,在类加载时,对象可能还没有创建
//所有静态方法和静态属性使用了泛型,JVM 就无法完成初始化
/* static R r2;
public static void m1(M m){
}*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public R getR() {//返回类型使用泛型
return r;
}
public void setR(R r) {//
this.r = r;
}
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
- Tiger后面有泛型,所有我们把Tiger就称为 自定义泛型类
T,R,M为
泛型的标识符,一般是单个大写字母- 泛型标识符可以有多个
自定义泛型接口
基本语法
interface 接口名<T,R...>{
}
package com.hspedu.generic.cusotmgeneric;
/**
* @author DL5O
* @version 1.0
*/
public class CustomInterfaceGeneric {
public static void main(String[] args) {
}
}
/**
* 泛型接口的注意事项和说明
* 1.接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
*/
//在继承接口 指定泛型接口的类型
interface IA extends IUsb<String,Double>{
}
//当我们去实现IA 接口时,因为IA 在继承IUsb时指定了泛型的类型
// U:String, R:Double
//在实现IUsb接口的方法时,使用String 替换U,使用Double替换R
class AA implements IA{
@Override
public Double get(String s) {
return null;
}
@Override
public void hi(Double aDouble) {
}
@Override
public void run(Double r1, Double r2, String u1, String u2) {
}
}
//实现接口时,直接指定泛型接口
//给U指定了Integer ,给R指定了Float
//所以,当我们实现IUsb方法时,会使用Integer替换U,使用Float替换R
class BB implements IUsb<Integer,Float>{
@Override
public Float get(Integer integer) {
return null;
}
@Override
public void hi(Float aFloat) {
}
@Override
public void run(Float r1, Float r2, Integer u1, Integer u2) {
}
}
//没有指定类型,默认为Object
//Object 会替换 U,R
class CC implements IUsb<Object,Object>{
@Override
public Object get(Object o) {
return null;
}
@Override
public void hi(Object o) {
}
@Override
public void run(Object r1, Object r2, Object u1, Object u2) {
}
}
interface IUsb<U,R>{
int n = 10;
//U name =""; 不能使用
//在普通方法中,可以使用泛型接口
R get(U u);
void hi(R r);
void run(R r1, R r2 , U u1, U u2);
//在jdk8.0中,可以在接口中,使用默认方法,也是可以使用泛型的
default R method(U u){
return null;
}
}
注意细节:
- 接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
- 在接口中的属性都是静态的,公开的,finale的
- 泛型接口的类型,在继承接口或者实现接口时确定
- 没有指定类型,默认为Object
自定义泛型方法
基本语法
修饰符 <T,R..>返回类型 方法名(参数列表){
}
package com.hspedu.generic.cusotmgeneric;
import java.util.ArrayList;
/**
* @author DL5O
* @version 1.0
*/
public class CustomGenericMethodGeneric {
public static void main(String[] args) {
Car car = new Car();
car.fly("你好", 10);//这里的是10会进行自动装箱
//当调用方法时,传入参数,编译器,就会确定类型
System.out.println("=============");
car.fly(300, 10.3);
System.out.println("=============");
//T->String R->ArrayList
Fish<String, ArrayList> fish = new Fish<>();
fish.hello(new ArrayList(),11.3f);
}
}
//泛型方法,可以定义在普通类中,也可以定义在泛型类中
class Car {//非常普通的类
public void run() {//普通方法
}
//解读
//1.<T,R> 为泛型标识符
//2.提供给 fly 使用的
public <T, R> void fly(T t, R r) {//泛型方法
System.out.println(t.getClass());//String
System.out.println(r.getClass());//Integer
}
}
class Fish<T, R> {//泛型类
public void run() {
}
public <U, M> void eat(U u, M m) {//泛型方法
}
//说明
//1.下面hi方法不是泛型方法
//2.是hi方法使用了类声明的泛型 T
public void hi(T t) {
}
//泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
public <K> void hello(R r,K k) {
System.out.println(r.getClass());//ArrayList
System.out.println(k.getClass());//
}
}
注意细节:
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会确定
public void eat(Ee){},
修饰符后没有<T,R..>,eat方法不是泛型方法,而是使用了泛型- 泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
七、泛型的继承和通配符
-
泛型不具备继承性
List<Object> list = new ArrayList<String>();//不正确
- :支持任意泛型类型
- :**支持A类以及A类的子类,规定了泛型的上限**
- :**支持A类以及A类的父类,不限于直接父类,规定了泛型的下限**
package com.hspedu.generic;
import java.util.ArrayList;
import java.util.List;
/**
* @author DL5O
* @version 1.0
* 泛型的继承的通配符
*/
public class GenericExtends {
public static void main(String[] args) {
Object xx = new String("xx");
//泛型没有继承性
// List<Object> list = new ArrayList<String>();//不正确
//举例说明下面三个方法的使用
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<AA> list3 = new ArrayList<>();
List<BB> list4 = new ArrayList<>();
List<CC> list5 = new ArrayList<>();
//如果是List<?> 可以接受任意的泛型类型
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);
//如果是printCollection2 List<? extends AA>
// printCollection2(list1); 语法通不过Object 不是 AA的子类
// printCollection2(list2); 语法通不过String 不是 AA的子类
printCollection2(list3);
printCollection2(list4);
printCollection2(list5);
//printCollection3(List<? super AA> c)
//接受AA 和它的父类,不限于直接父类
printCollection3(list1);
// printCollection3(list2); String 和AA 没有任何关系
printCollection3(list3);
// printCollection3(list4); BB 为 AA 的子类
// printCollection3(list5); CC 为 AA 的子类
}
//说明: List<?> 表示任意的泛型类型都可以接收
public static void printCollection1(List<?> c) {
for (Object object : c) {
System.out.println(object);
}
}
//List<? extends AA> 表示上限,可以接受AA 或 AA的子类
public static void printCollection2(List<? extends AA> c) {
for (Object object : c) {
System.out.println(object);
}
}
//List<? super AA> 子类名AA : 支持AA类以及AA类的父类,不限于直接父类
//规定了泛型的下限
public static void printCollection3(List<? super AA> c) {
for (Object object : c) {
System.out.println(object);
}
}
}
class AA {
}
class BB extends AA {
}
class CC extends BB {
}
本章作业
Homework01
package com.hspedu.generic.Homewrok;
import java.util.*;
/**
* @author DL5O
* @version 1.0
*/
public class Homework01 {
public static void main(String[] args) {
User ywl = new User(1, 21, "ywl");
User sky = new User(2, 20, "sky");
User zsj = new User(3, 19, "zsj");
User wjq = new User(4, 18, "wjq");
User py = new User(4, 20, "py");
//sava方法的使用
DAO<User> userDAO = new DAO<>();
userDAO.save("1", ywl);
userDAO.save("2", sky);
userDAO.save("3", zsj);
userDAO.save("4", wjq);
System.out.println("====添加之后====");
//list方法的使用
List<User> list = userDAO.list();
DAO.output(list);
System.out.println("=====修改之后====");
//update函数的使用
userDAO.update("4", py);
DAO.output(list);
System.out.println("====1号ID用户的信息====");
System.out.println(userDAO.get("1"));
System.out.println("====删除之后====");
userDAO.delete("1");
list = userDAO.list();
DAO.output(list);
}
}
DAO
package com.hspedu.generic.Homewrok;
import java.util.*;
/**
* @author DL5O
* @version 1.0
*/
public class DAO<T>{
private Map<String,T> map = new HashMap<>();
//保存t对象到Map 成员变量中
public void save(String id,T entity){
map.put(id,entity);
}
//获取id对应的对象
public T get(String id){
return map.get(id);
}
//替换map中的key为id的内容,改为entity对象
public void update(String id,T entity){
map.replace(id,entity);
// map.put(id,entity);
}
//返回map中存放的所有t对象
public List<T> list(){
//创建ArrayList
List<T> list = new ArrayList<>();
//第一种
/*Set<String> keySet = map.keySet();
for (String key: keySet) {
list.add(map.get(key));
}*/
//第二种
Collection<T> values = map.values();
Iterator<T> iterator = values.iterator();
while (iterator.hasNext()) {
T t = iterator.next();
list.add(t);
}
return list;
}
/*public Collection<T> list(){
return map.values();
}*/
//删除指定的id对象
public void delete(String id){
map.remove(id);
}
//输出
public static void output(List<User> list) {
Iterator<User> iterator = list.iterator();
while (iterator.hasNext()) {
User user = iterator.next();
System.out.println(user);
}
}
}
User
package com.hspedu.generic.Homewrok;
/**
* @author DL5O
* @version 1.0
*/
public class User {
private int id;
private int age;
private String name;
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}