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 {
}


相关文章
|
2月前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
28 0
[Java]泛型
|
2月前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
15 1
|
2月前
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
27 5
|
2月前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
19 1
|
3月前
|
Java 编译器 容器
Java——包装类和泛型
包装类是Java中一种特殊类,用于将基本数据类型(如 `int`、`double`、`char` 等)封装成对象。这样做可以利用对象的特性和方法。Java 提供了八种基本数据类型的包装类:`Integer` (`int`)、`Double` (`double`)、`Byte` (`byte`)、`Short` (`short`)、`Long` (`long`)、`Float` (`float`)、`Character` (`char`) 和 `Boolean` (`boolean`)。包装类可以通过 `valueOf()` 方法或自动装箱/拆箱机制创建。
39 9
Java——包装类和泛型
|
2月前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
20 2
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
2月前
|
安全 Java 编译器
Java基础-泛型机制
Java基础-泛型机制
16 0
|
3月前
|
存储 安全 搜索推荐
Java中的泛型
【9月更文挑战第15天】在 Java 中,泛型是一种编译时类型检查机制,通过使用类型参数提升代码的安全性和重用性。其主要作用包括类型安全,避免运行时类型转换错误,以及代码重用,允许编写通用逻辑。泛型通过尖括号 `&lt;&gt;` 定义类型参数,并支持上界和下界限定,以及无界和有界通配符。使用泛型需注意类型擦除、无法创建泛型数组及基本数据类型的限制。泛型显著提高了代码的安全性和灵活性。
|
2月前
|
Java
【Java】什么是泛型?什么是包装类
【Java】什么是泛型?什么是包装类
18 0