泛型概述和举例

1. 泛型的引入

在Java中,我们在声明方法时,当在完成方法功能时如果有未知的数据需要参与,这些未知的数据需要在调用方法时才能确定,那么我们把这样的数据通过形参表示。在方法体中,用这个形参名来代表那个未知的数据,而调用者在调用时,对应的传入实参就可以了。

image-202315405807555

受以上启发,JDK1.5设计了泛型的概念。泛型即为“类型参数”,这个类型参数在声明它的类、接口或方法中,代表未知的某种通用类型。

举例1:

集合类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK5.0之前只能把元素类型设计为Object,JDK5.0时Java引入了“参数化类型(Parameterized type)”的概念,允许我们在创建集合时指定集合元素的类型。比如:List<String>,这表明该List只能保存字符串类型的对象。

使用集合存储数据时,除了元素的类型不确定,其他部分是确定的(例如关于这个元素如何保存,如何管理等)。

举例2:

java.lang.Comparable接口和java.util.Comparator接口,是用于比较对象大小的接口。这两个接口只是限定了当一个对象大于另一个对象时返回正整数,小于返回负整数,等于返回0,但是并不确定是什么类型的对象比较大小。JDK5.0之前只能用Object类型表示,使用时既麻烦又不安全,因此 JDK5.0 给它们增加了泛型。

image-20220923154058074

image-20220923154426871

其中<T>就是类型参数,即泛型。

所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值或参数的类型。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)确定(即传入实际的类型参数,也称为类型实参)。

2. 使用泛型举例

自从JDK5.0引入泛型的概念之后,对之前核心类库中的API做了很大的修改,例如:JDK5.0改写了集合框架中的全部接口和类、java.lang.Comparable接口、java.util.Comparator接口、Class类等。为这些接口、类增加了泛型支持,从而可以在声明变量、创建对象时传入类型实参。

2.1 集合中使用泛型

2.1.1 举例

集合中没有使用泛型时:

image-20220411001522636

集合中使用泛型时:

image-20220411001549747

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。即,把不安全的因素在编译期间就排除了,而不是运行期;既然通过了编译,那么类型一定是符合要求的,就避免了类型转换。

同时,代码更加简洁、健壮。

把一个集合中的内容限制为一个特定的数据类型,这就是generic背后的核心思想。

举例:

//泛型在List中的使用

@Test  
public void test1(){  
    //举例:将学生成绩保存在ArrayList中  
    //标准写法:  
    //ArrayList<Integer> list = new ArrayList<Integer>();  
    //jdk7的新特性:类型推断  
    ArrayList<Integer> list = new ArrayList<>();  
​  
    list.add(56); //自动装箱  
    list.add(76);  
    list.add(88);  
    list.add(89);  
    //当添加非Integer类型数据时,编译不通过  
    //list.add("Tom");//编译报错  
​  
    Iterator<Integer> iterator = list.iterator();  
    while(iterator.hasNext()){  
        //不需要强转,直接可以获取添加时的元素的数据类型  
        Integer score = iterator.next();  
        System.out.println(score);  
    }  
}

举例:

//泛型在Map中的使用  
@Test  
public void test2(){  
    HashMap<String,Integer> map = new HashMap<>();  
​  
    map.put("Tom",67);  
    map.put("Jim",56);  
    map.put("Rose",88);  
    //编译不通过  
    //        map.put(67,"Jack");  
​  
    //遍历key集  
    Set<String> keySet = map.keySet();  
    for(String str:keySet){  
        System.out.println(str);  
    }  
​  
    //遍历value集  
    Collection<Integer> values = map.values();  
    Iterator<Integer> iterator = values.iterator();  
    while(iterator.hasNext()){  
        Integer value = iterator.next();  
        System.out.println(value);  
    }  
​  
    //遍历entry集  
    Set<Map.Entry<String, Integer>> entrySet = map.entrySet();  
    Iterator<Map.Entry<String, Integer>> iterator1 = entrySet.iterator();  
    while(iterator1.hasNext()){  
        Map.Entry<String, Integer> entry = iterator1.next();  
        String key = entry.getKey();  
        Integer value = entry.getValue();  
        System.out.println(key + ":" + value);  
    }  
​  
}

2.1.2 练习

练习1:

(1)创建一个ArrayList集合对象,并指定泛型为<Integer>

(2)添加5个[0,100)以内的整数到集合中

(3)使用foreach遍历输出5个整数

(4)使用集合的removeIf方法删除偶数,为Predicate接口指定泛型<Ineteger>

(5)再使用Iterator迭代器输出剩下的元素,为Iterator接口指定泛型<Integer>

package com.atguigu.genericclass.use;  
​  
import java.util.ArrayList;  
import java.util.Iterator;  
import java.util.Random;  
import java.util.function.Predicate;  
​  
public class TestNumber {  
    public static void main(String[] args) {  
        ArrayList<Integer> coll = new ArrayList<Integer>();  
        Random random = new Random();  
        for (int i = 1; i <= 5 ; i++) {  
            coll.add(random.nextInt(100));  
        }  
​  
        System.out.println("coll中5个随机数是:");  
        for (Integer integer : coll) {  
            System.out.println(integer);  
        }  
          
        //方式1:使用集合的removeIf方法删除偶数  
        coll.removeIf(new Predicate<Integer>() {  
            @Override  
            public boolean test(Integer integer) {  
                return integer % 2 == 0;  
            }  
        });  
        //方式2:调用Iterator接口的remove()方法  
        //Iterator<Integer> iterator1 = coll.iterator();  
        //while(coll.hasNext()){  
        //    Integer i = coll.next();  
        //   if(i % 2 == 0){  
        //       coll.remove();  
        //    }  
        //}  
​  
        System.out.println("coll中删除偶数后:");  
        Iterator<Integer> iterator = coll.iterator();  
        while(iterator.hasNext()){  
            Integer number = iterator.next();  
            System.out.println(number);  
        }  
​  
    }  
}

2.2 比较器中使用泛型

2.2.1 举例

package com.atguigu.generic;  
​  
public class Circle{  
    private double radius;  
​  
    public Circle(double radius) {  
        super();  
        this.radius = radius;  
    }  
​  
    public double getRadius() {  
        return radius;  
    }  
​  
    public void setRadius(double radius) {  
        this.radius = radius;  
    }  
​  
    @Override  
    public String toString() {  
        return "Circle [radius=" + radius + "]";  
    }  
​  
}

使用泛型之前:

package com.atguigu.generic;  
​  
import java.util.Comparator;  
​  
class CircleComparator implements Comparator{  
    @Override  
    public int compare(Object o1, Object o2) {  
        //强制类型转换  
        Circle c1 = (Circle) o1;  
        Circle c2 = (Circle) o2;  
        return Double.compare(c1.getRadius(), c2.getRadius());  
    }  
}  
//测试:  
public class TestNoGeneric {  
    public static void main(String[] args) {  
        CircleComparator com = new CircleComparator();  
        System.out.println(com.compare(new Circle(1), new Circle(2)));  
​  
        System.out.println(com.compare("圆1", "圆2"));//运行时异常:ClassCastException  
    }  
}

使用泛型之后:

package com.atguigu.generic;  
​  
import java.util.Comparator;  
​  
class CircleComparator1 implements Comparator<Circle> {  
​  
    @Override  
    public int compare(Circle o1, Circle o2) {  
        //不再需要强制类型转换,代码更简洁  
        return Double.compare(o1.getRadius(), o2.getRadius());  
    }  
}  
​  
//测试类  
public class TestHasGeneric {  
    public static void main(String[] args) {  
        CircleComparator1 com = new CircleComparator1();  
        System.out.println(com.compare(new Circle(1), new Circle(2)));  
​  
        //System.out.println(com.compare("圆1", "圆2"));  
        //编译错误,因为"圆1", "圆2"不是Circle类型,是String类型,编译器提前报错,  
        //而不是冒着风险在运行时再报错。  
    }  
}

2.2.2 练习

(1)声明矩形类Rectangle,包含属性长和宽,属性私有化,提供有参构造、get/set方法、重写toString方法,提供求面积和周长的方法。

(2)矩形类Rectangle实现java.lang.Comparable<T>接口,并指定泛型为<Rectangle>,重写int compareTo(T t)方法,按照矩形面积比较大小,面积相等的,按照周长比较大小。

(3)在测试类中,创建Rectangle数组,并创建5个矩形对象

(4)调用Arrays的sort方法,给矩形数组排序,并显示排序前后的结果。

package com.atguigu.genericclass.use;  
​  
public class Rectangle implements Comparable<Rectangle>{  
    private double length;  
    private double width;  
​  
    public Rectangle(double length, double width) {  
        this.length = length;  
        this.width = width;  
    }  
​  
    public double getLength() {  
        return length;  
    }  
​  
    public void setLength(double length) {  
        this.length = length;  
    }  
​  
    public double getWidth() {  
        return width;  
    }  
​  
    public void setWidth(double width) {  
        this.width = width;  
    }  
    //获取面积  
    public double area(){  
        return length * width;  
    }  
    //获取周长  
    public double perimeter(){  
        return 2 * (length + width);  
    }  
​  
    @Override  
    public String toString() {  
        return "Rectangle{" +  
                "length=" + length +  
                ", width=" + width +  
                ",area =" + area() +  
                ",perimeter = " + perimeter() +  
                '}';  
    }  
​  
    @Override  
    public int compareTo(Rectangle o) {  
        int compare = Double.compare(area(), o.area());  
        return compare != 0 ? compare : Double.compare(perimeter(),o.perimeter());  
    }  
}  
​

package com.atguigu.genericclass.use;  
​  
import java.util.Arrays;  
​  
public class TestRectangle {  
    public static void main(String[] args) {  
        Rectangle[] arr = new Rectangle[4];  
        arr[0] = new Rectangle(6,2);  
        arr[1] = new Rectangle(4,3);  
        arr[2] = new Rectangle(12,1);  
        arr[3] = new Rectangle(5,4);  
​  
        System.out.println("排序之前:");  
        for (Rectangle rectangle : arr) {  
            System.out.println(rectangle);  
        }  
​  
        Arrays.sort(arr);  
​  
        System.out.println("排序之后:");  
        for (Rectangle rectangle : arr) {  
            System.out.println(rectangle);  
        }  
    }  
}  
​

2.3 相关使用说明

  • 在创建集合对象的时候,可以指明泛型的类型。

    具体格式为:List<Integer> list = new ArrayList<Integer>();

  • JDK7.0时,有新特性,可以简写为:

    List<Integer> list = new ArrayList<>(); //类型推断

  • 泛型,也称为泛型参数,即参数的类型,只能使用引用数据类型进行赋值。(不能使用基本数据类型,可以使用包装类替换)
  • 集合声明时,声明泛型参数。在使用集合时,可以具体指明泛型的类型。一旦指明,类或接口内部,凡是使用泛型参数的位置,都指定为具体的参数类型。如果没有指明的话,看做是Object类型。
Java基础

Collections工具类

2025-8-13 7:10:59

Java基础

自定义泛型

2025-8-15 15:00:00

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索