各自努力,最高处见!加油!
泛型
-
编译时,约束加入集合中的元素类型,提高了安全性。如果编译器发现添加的类型不满足要求,就会报错。
-
减少了类型的转换次数。在遍历的时候,可以直接取出该类型,而不是先取出Object对象再向下转型。
一、泛型的介绍
- 泛型又称参数化类型,是JDK5.0出现的新特性,解决数据类型的安全问题。
- 在类声明或实例化时只要指定好需要的具体类型即可。
- Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastExecption异常。同时,代码更加健壮、简洁。
- 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值类型,或者是参数类型。
//E 表示s的数据类型,该数据类型在定义Person对象时再指定,即在编译期间就知道E是什么类型。
class Person<E>{
public Person(E s){
this.s=s;
}
public E f(){
return s;
}
}
二、泛型的语法
声明
interface 接口<T>{}
class 类<K,V>{}
- K,T,V不代表值,而表示类型。
- 任意字母都可以,常用T表示,是Type的缩写。
泛型的实例化:
1.List<String> strList = new ArrayList<String>();
2.Iterator<Customer> iterator=customers.iterator();
泛型使用的注意事项和细节
- interface List< T >{},public class HashSet< E >{}
说明:T,E只能是引用类型。
List< Integer > list=new ArrayList< Integer >;
List< int > list1=new ArrayList< int >;//错误
- 在指定泛型的具体类型后,可以传入该类型或其子类类型。
- 泛型的使用形式
ArrayList<Integer> list1=new ArrayList<Integer>();
List<Integer> list2=new ArrayList<Integer>();
//在实际开发中,往往采用简写(右边不用写泛型)。编译器会进行类型推断,推荐用简写。
ArrayList<Integer> list1=new ArrayList<>();
List<Integer> list2=new ArrayList<>();
//未学习泛型的写法:默认给它的泛型是Object
ArrayList list1=new ArrayList();
List list2=new ArrayList();
示例代码
import java.util.ArrayList;
import java.util.Comparator;
public class Homework1 {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
Employee jack = new Employee("jack", 1000, new MyDate(2021, 10, 1));
Employee tom = new Employee("tom", 2000, new MyDate(2021, 10, 2));
Employee smith = new Employee("smith", 3000, new MyDate(2021, 10, 3));
Employee smith2 = new Employee("smith", 3000, new MyDate(2021, 10, 4));
employees.add(jack);
employees.add(tom);
employees.add(smith);
employees.add(smith2);
employees.sort(new Comparator<Employee>() {
@Override
// public int compare(Employee employee, Employee t1) {//自己写的
// if (employee.getName()==t1.getName()){
// if(employee.getBirthday().getYear()==t1.getBirthday().getYear()){
// if (employee.getBirthday().getMonth()==t1.getBirthday().getMonth()){
// if (employee.getBirthday().getDay()==t1.getBirthday().getDay()){
// return 0;
// }
// }else {
// return -1;
// }
// }else {
// return -1;
// }
// }else {
// return employee.getName().compareTo(t1.getName());//判断字符串字母的顺序
// }
// return 0;
// }
public int compare(Employee employee, Employee t1) {//韩顺平老师的示例代码:不用很多层if-else语句,可读性好
int i=employee.getName().compareTo(t1.getName());
if (i!=0){
return i;
}
//如果name相同,出生时间:这段代码最好让MyDate实现
// int yearMinus=employee.getBirthday().getYear()-t1.getBirthday().getYear();
// if (yearMinus!=0){
// return yearMinus;
// }
//
// int monthMinus=employee.getBirthday().getMonth()-t1.getBirthday().getMonth();
// if (monthMinus!=0){
// return monthMinus;
// }
//
// int dayMinus=employee.getBirthday().getDay()-t1.getBirthday().getDay();
// return dayMinus;
return employee.getBirthday().compareTo(t1.getBirthday());//在MyDate中实现Comparator比较
}
});
System.out.println(employees);
}
}
class Employee{
private String name;
private int money;
private MyDate birthday;
public Employee(String name, int money, MyDate birthday) {
this.name = name;
this.money = money;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", money=" + money +
", birthday=" + birthday +
'}';
}
}
class MyDate implements Comparable<MyDate>{
private int month;
private int day;
private int year;
public MyDate(int year,int month, int day) {
this.month = month;
this.day = day;
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;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "MyDate{" +
"month=" + month +
", day=" + day +
", year=" + year +
'}';
}
// MyDate实现时间的比较
@Override
public int compareTo(MyDate myDate) {
int yearMinus=year-myDate.getYear();
if (yearMinus!=0){
return yearMinus;
}
int monthMinus=month-myDate.getMonth();
if (monthMinus!=0){
return monthMinus;
}
int dayMinus=day-myDate.getDay();
return dayMinus;
}
}
三、自定义泛型类
基本语法
class 类名<T,R...>{
成员
}
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型,是在创建对象是确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认为Object
示例代码:
public class CustomGeneric_ {
public static void main(String[] args) {
Tiger tiger=new Tiger("John");//没有指定泛型的类型,全部默认为Object
}
}
class Tiger<T,R,M>{
String name;
T t;//属性使用泛型
M m;
R r;
// T[] ts=new T[8];//错误写法,泛型数组不能实例化。数组在new的时候不确定T的类型,不知道开辟多大的内存空间
public Tiger(String name) {
this.name = name;
}
public Tiger(String name, T t, M m, R r) {//构造器使用泛型
this.name = name;
this.t = t;
this.m = m;
this.r = r;
}
//静态方法中不能使用类的泛型。因为静态和类相关,在类加载的时,对象还没创建,
//所以,如果静态方法和静态属性使用了泛型,JVM无法完成初始化。
//简单来说:泛型的确定在构建对象的时候,但是静态的属性和方法在类编译时就要生成,此时编译器不知道类型是什么,所以无法创建变量。
// public static void m1(M m){//错误写法
//
// }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
public R getR() {
return r;
}
public void setR(R r) {
this.r = r;
}
}
四、自定义泛型接口
基本语法
interface 接口名<T,R>{
}
- 接口中,静态成员也不能使用泛型
- 泛型接口的类型,在继承接口或实现接口时确定
- 没有指定类型,默认为Object
示例代码:
public class CustomInterfaceGeneric {
public static void main(String[] args) {
}
}
interface IUsb<U,R>{
int n=10;//正常的属性赋值
// U name="";//在接口中不允许使用泛型定义属性
R get(U u);//普通方法中,可以使用接口泛型
void f(R r);
//在jdk8中,可以在接口中使用默认方法,方法中可以使用泛型
default R method(U u){
return null;
}
}
//在实现接口的时候没有指定泛型的类型,系统默认为Object类型,在方法中全部用Object类型替换泛型。
class C implements IUsb{
@Override
public Object get(Object o) {
return null;
}
@Override
public void f(Object o) {
}
@Override
public Object method(Object o) {
return null;
}
}
interface IA extends IUsb<String,Double>{//要在继承的时候确定泛型
}
//当我们实现IA接口时,因为IA在继承IUsb接口时,只定了U为String R为Double
//所在实现IUsb接口的方法时,使用String替换U,使用Double替换R
class A implements IA{
@Override
public Double get(String s) {
return null;
}
@Override
public void f(Double aDouble) {
}
@Override
public Double method(String s) {
return null;
}
}
五、自定义泛型方法
基本语法
修饰符<T,R...>返回类型 方法名(参数列表){
}
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会被确定
- public void eat(E e){}修饰符后面没有<T,R…> ==>eat方法不是泛型方法,而是eat方法使用了泛型
- 泛型类中定义泛型方法。泛型方法可以使用类声明的泛型,也可以使用自己的泛型
示例代码:
import java.util.ArrayList;
public class CustomMethodGeneric {
public static void main(String[] args) {
Car car=new Car();
car.run("宝马",1000);//普通类中定义的泛型方法,在传入参数的时候,编译器会自动确定类型
car.run(10,1000);//输入的是int型变量,编译器会自动装箱,泛型类型为Integer
Fish<String, ArrayList> fish = new Fish<>();
fish.eat("jack",100,new ArrayList());
}
}
//泛型方法可以定义在普通类中,也可以定义在泛型类中
class Car{
public void run(){//普通方法
}
public <T,R> void run(R r,T t){//泛型方法:注意泛型定义符的位置
System.out.println(t.getClass());
System.out.println(r.getClass());
}
}
class Fish<T,R>{//泛型类
public void run(){
}
public<U,M> void eat(U u,M m,R r){//泛型类中定义泛型方法。泛型方法可以使用类声明的泛型,也可以使用自己的泛型
System.out.println(u.getClass());
System.out.println(m.getClass());
System.out.println(r.getClass());
}
public void f(T t){//f方法不是泛型方法,f方法只是使用了类声明的方法
}
}
六、泛型的继承和通配符
1.泛型不具备继承性
List<Object> list = new ArrayList<String>();//错误,泛型不具备继承性
2.<?>:支持任意泛型
3.<? extends A>:支持A类以及A类的子类,规定了泛型的上限
4.<? super A>:支持A类以及A类的父类,不限于父类,规定了泛型的下限
示例代码:
import java.util.ArrayList;
import java.util.List;
public class GenericExtends {
public static void main(String[] args) {
List<Object> objectList = new ArrayList<>();
List<String> stringList = new ArrayList<>();
List<SA> SAList = new ArrayList<>();
List<AA> AAList = new ArrayList<>();
List<BB> BBList = new ArrayList<>();
printCollection1(objectList);//可以传入任何类型的List
printCollection1(stringList);
printCollection1(AAList);
printCollection2(AAList);//只能传入AA类型或AA的子类
printCollection2(BBList);
printCollection3(objectList);//只能传入AA的父类,但不限制为直接父类
printCollection3(AAList);
// printCollection3(BB);//错误,
}
public static void printCollection1(List<?> c){//任何类型都可以
}
public static void printCollection2(List<? extends AA> c){//任何类型都可以
}
public static void printCollection3(List<? super AA> c){//任何类型都可以
}
}
class SA{ }
class AA extends SA{}
class BB extends AA{}
七、练习题
找错误
错误:eat方法中,没有泛型标识符,为一般方法使用泛型变量,但是它使用的泛型变量在类中并没有被定义,所以错误。
泛型的综合练习
注意点:如何获取Map对象中存放的所有T对象。(遍历取出)
实现代码:
import org.junit.jupiter.api.Test;
import java.util.*;
public class Homework {
public static void main(String[] args) {
}
@Test
public void test(){
DAO<User> userDAO = new DAO<>();
userDAO.save("001",new User(1,18,"jack"));//value中存的是User对象
userDAO.save("002",new User(2,28,"tom"));
userDAO.save("003",new User(3,19,"smith"));
List<User> list=userDAO.list();
System.out.println("list:"+list);
System.out.println("id为002的对象:"+userDAO.get("002"));
userDAO.update("001",new User(1,56,"abc"));
List<User> list2=userDAO.list();
System.out.println("list2:"+list2);
userDAO.delete("002");
List<User> list3=userDAO.list();
System.out.println("list3:"+list3);
}
}
class DAO<T>{
Map<String,T> map=new HashMap<String , T>();
public void save(String id,T entity){//保存T对象到Map成员变量中
map.put(id,entity);
}
public T get(String id){//从map中获取id对应的对象
return map.get(id);
}
public void update(String id,T entity){//替换map中key为id的内容,改为entity对象
map.put(id,entity);
}
public List<T> list(){//返回map中存放的所有T对象
ArrayList<T> arrayList = new ArrayList<>();
//遍历取出map中的所有value
for (T t : map.values()) {
arrayList.add(t);
}
return arrayList;
}
public void delete(String id){//删除指定id对象
map.remove(id);
}
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}