Java进阶详解(六)--->泛型

简介: 从 0 开始学 Java 知识,并不定期更新所学笔记,期待一年后的蜕变吧! <有同样想法的小伙伴,可以联系我一起交流学习哦!>

什么是泛型

  • Java泛型(generics)是 JDK 5 中引入的一个新特性,在很大的程度上方便在集合上的使用。
  • 泛型的本质是 参数化类型,最熟悉的就是定义方法的时候需要形参,调用方法的时候,需要传递实参。那”参数化类型”就是将原来具体的类型参数化

泛型目的

  • 泛型的出现避免了在运行时强制类型转换而出现 ClassCastException (类型转化异常)。

泛型引出

请编写程序,在 ArrayList 中,添加 3 个 Dog 对象,Dog 对象含有 name 和 age,并输出 name 和age (要求使用getXxx())

传统方法



package com.jwt.generic;
import java.util.ArrayList;
public class Generic01 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add(new Dog("小黄",10));
        arrayList.add(new Dog("小白",2));
        arrayList.add(new Dog("小黑",5));
        for (Object o : arrayList){
            Dog dog = (Dog) o;//需要向下转型Object ->Dog
            System.out.println(dog.getName());
        }
    }
}
class Dog {
    private String name;
    private int age;
    public Dog() {
    }
    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;
    }
}

使用泛型



package com.jwt.generic;
import java.util.ArrayList;
public class Generic02 {
    public static void main(String[] args) {
        ArrayList<Dog> arrayList = new ArrayList<>();
        arrayList.add(new Dog("小黄",10));
        arrayList.add(new Dog("小白",2));
        arrayList.add(new Dog("小黑",5));
        for (Dog dog : arrayList){
            System.out.println(dog.getName());
        }
    }
}
class Dog {
    private String name;
    private int age;
    public Dog() {
    }
    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;
    }
}

泛型优点

  • 编译时,检查添加元素的类型,提高了安全性
  • 不再提示编译警告
  • 减少了类型转换的次数,提高效率
  • 不使用泛型 Dog -> Object -> Dog //放入到 ArrayList 会先转成 Object,在取出时,还需要转换成 Dog
  • 使用泛型 Dog-> Dog -> Dog //放入和取出时,不需要类型转换,提高效率

泛型使用

泛型有三种常用的使用方式:泛型类,泛型接口和泛型方法

泛型类



class Person<E>{//如果创建对象时没有指定类型,默认是 Object
                E s;
                public Person(E s) {
                this.s = s;
                }
}
  • 泛型类型必须是引用类型(非基本数据类型)

List<Integer> list = new ArrayList<Integer>(); //正确
List<int> list2 = new ArrayList<int>();//错误
  • 泛型的使用形式

List<Integer> list = new List<Integer>();
List<Integer> list = new List<>();//推荐使用简写
  • 静态方法和静态属性不能使用泛型
  • 泛型默认是Object

List List = new List();
//等价
List<Object> List = new List<>();
  • 由于使用自定义泛型的类,只有在实例化的时候才知道这个类型参数是什么,所以导致
  • (1)使用泛型的数组不能初始化;
  • (2)静态方法和静态属性并不能使用泛型。

泛型接口



public interface Test_ <E>{
    //...
}

泛型接口的类型,在继承接口 或者 实现接口时确定。(默认Object)

例子



/**
 * 泛型接口的定义格式:  修饰符  interface 接口名<数据类型> {}
 */
public interface Inter<T> {
    public abstract void show(T t) ;
}
/**
 * 子类是泛型类
 */
public class InterImpl<E> implements Inter<E> {
    @Override
    public void show(E t) {
        System.out.println(t);
    }
}
Inter<String> inter = new InterImpl<String>() ;
inter.show("hello") ;

泛型方法



public <E> void Come(E e){
   //...
}
  • 泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型

class Fish<T, R> {//泛型类
  public void run() {//普通方法
  }
  public<U,M> void eat(U u, M m) {//泛型方法
  }
  //说明
  //1. 下面hi 方法不是泛型方法
  //2. 是hi 方法使用了类声明的泛型
  public void hi(T t) {
  }
  //泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
  public<K> void hello(R r, K k) {
  System.out.println(r.getClass());//ArrayList
  System.out.println(k.getClass());//Float
  }
}

例子



class Demo{  
  public <T> T fun(T t){   // 可以接收任意类型的数据  
   return t ;     // 直接把参数返回  
  }  
};  
public class GenericsDemo{  
  public static void main(String args[]){  
    Demo d = new Demo() ; // 实例化Demo对象  
    String str = d.fun("汤姆") ; // 传递字符串  
    int i = d.fun(30) ;  // 传递数字,自动装箱  
    System.out.println(str) ; // 输出内容  
    System.out.println(i) ;  // 输出内容  
  }  
};

源码中泛型使用

下面是List接口和ArrayList类的代码片段



//定义接口时指定了一个类型形参,该形参名为E
public interface List<E> extends Collection<E> {
   //在该接口里,E可以作为类型使用
   public E get(int index) {}
   public void add(E e) {} 
}
//定义类时指定了一个类型形参,该形参名为E
public class ArrayList<E> extends AbstractList<E> implements List<E> {
   //在该类里,E可以作为类型使用
   public void set(E e) {
   .......................
   }
}

实例

  • 定义Employee类
  • 该类包含: private成员变量name,sal,birthday, 其中birthday为MyDate类的对象;
  • 为每一个属性定义getter, setter方法;
  • 重写toString方法输出name, sal, birthday
  • 定义MyDate类
  • 包含: private成员变量month,day,year;
  • 并为每一个属性定义getter,setter方法;
  • 创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList需使用泛型来定义)
  • 对集合中的元素进行排序,并遍历输出
  • 排序方式:调用ArrayList 的sort方法,传入Comparator对象[使用泛型],先按照
    name排序,如果name相同,则按生日日期的先后排序。 即:定制排序

Employee.java



package com.jwt.generic;
public 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 "\nEmployee{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }
}

MyDate.java



package com.jwt.generic;
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) {
        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();
    }
}

Main.java



package com.jwt.generic;
import java.util.ArrayList;
import java.util.Comparator;
public class GenericExercise02
{
    public static void main(String[] args) {
        ArrayList<Employee> employees = new ArrayList<>();
        employees.add(new Employee("小明",10000, new MyDate(2000,7,2)));
        employees.add(new Employee("小红",20000, new MyDate(2002,10,2)));
        employees.add(new Employee("小明",30000, new MyDate(2000,6,2)));
        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;
            }
            //封装后,将来可维护性和复用性,就大大增强.
            return emp1.getBirthday().compareTo(emp2.getBirthday());
        }
        });
        System.out.println("==对雇员进行排序==");
        System.out.println(employees);
    }
}

泛型的继承和通配符

  • 泛型不具备继承性

//Object是String的父类
Object o = new String("xx");//正确
List<Object> list = new ArrayList <String>; //错误
  • 通配符
  • :支持任意泛型类型
  • :支持A类以及A类的子类,规定了泛型的上限
  • :支持A类以及A类的父类,不限于直接父类,规定了泛型的下限



package com.jwt.generic;
import java.util.ArrayList;
import java.util.List;
public class GenericExtends {
    public static void main(String[] args) {
        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<?> c ,可以接受任意的泛型类型
        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);
        //List<? extends AA> c: 表示上限,可以接受AA 或者AA 子类
        printCollection2(list1);//×
        printCollection2(list2);//×
        printCollection2(list3);//√
        printCollection2(list4);//√
        printCollection2(list5);//√
        //List<? super AA> c: 支持AA 类以及AA 类的父类,不限于直接父类
        printCollection3(list1);//√
        printCollection3(list2);//×
        printCollection3(list3);//√
        printCollection3(list4);//×
        printCollection3(list5);//×
    }
    //说明: List<?> 表示任意的泛型类型都可以接受
    public static void printCollection1(List<?> c) {
        for (Object object : c) { // 通配符,取出时,就是Object
            System.out.println(object);
        }
    }
    // ? extends ,可以接受AA 或者AA 子类
    public static void printCollection2(List<? extends AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }
    // ? super,支持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 {
}


相关文章
|
5天前
|
安全 Java 程序员
Java 泛型
Java 泛型
15 0
|
1天前
|
安全 Java API
Java一分钟之-泛型通配符:上限与下限野蛮类型
【5月更文挑战第19天】Java中的泛型通配符用于增强方法参数和变量的灵活性。通配符上限`? extends T`允许读取`T`或其子类型的列表,而通配符下限`? super T`允许向`T`或其父类型的列表写入。野蛮类型不指定泛型,可能引发运行时异常。注意,不能创建泛型通配符实例,也无法同时指定上下限。理解和适度使用这些概念能提升代码的通用性和安全性,但也需兼顾可读性。
22 3
|
2天前
|
安全 Java API
Java进阶-Java Stream API详解与使用
效、更易于维护的代码,同时享受到函数式编程带来的好处。
12 2
|
5天前
|
安全 Java 编译器
java泛型浅谈
java泛型浅谈
7 1
|
5天前
|
存储 安全 Java
Java一分钟之-集合框架进阶:Set接口与HashSet
【5月更文挑战第10天】本文介绍了Java集合框架中的`Set`接口和`HashSet`类。`Set`接口继承自`Collection`,特征是不允许重复元素,顺序不确定。`HashSet`是`Set`的实现,基于哈希表,提供快速添加、删除和查找操作,但无序且非线程安全。文章讨论了`HashSet`的特性、常见问题(如元素比较规则、非唯一性和线程安全性)以及如何避免这些问题,并提供了代码示例展示基本操作和自定义对象的使用。理解这些概念和注意事项能提升代码效率和可维护性。
13 0
|
5天前
|
存储 安全 Java
掌握8条泛型规则,打造优雅通用的Java代码
掌握8条泛型规则,打造优雅通用的Java代码
掌握8条泛型规则,打造优雅通用的Java代码
|
5天前
|
Java
JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识
【5月更文挑战第2天】JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识。入坑JAVA因它的面向对象特性、平台无关性、强大的标准库和活跃的社区支持。
42 2
|
5天前
|
安全 Java 编译器
【JAVA】泛型和Object的区别
【JAVA】泛型和Object的区别
|
5天前
|
存储 算法 Java
滚雪球学Java(20):Java泛型与枚举:提升代码灵活性与可读性
【4月更文挑战第9天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
30 1
滚雪球学Java(20):Java泛型与枚举:提升代码灵活性与可读性
|
5天前
|
Java