【JavaSE】泛型通关教程

简介: 文章目录1 泛型引入2 泛型讲解2.1 泛型的介绍2.2 泛型实例2.3 泛型使用细节3 自定义泛型类4 自定义泛型接口5 自定义泛型方法6 泛型的继承与通配写在最后

1 泛型引入

 我们来引入一个需求,在ArrayList集合中添加三个Dog对象,并遍历该集合,输出Dog对象的姓名。这点我们很容易做到,只需要在遍历的时候将Object对象向下转型为Dog就可以正常遍历了。但是,假如程序员在添加Dog对象时不小心添加了一只猫呢? 来看下面的代码:

import java.util.ArrayList;
/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 */
public class Generic0 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        Dog dog1 = new Dog("小黄");
        Dog dog2 = new Dog("小花");
        Cat cat = new Cat("猫猫");
        arrayList.add(dog1);
        arrayList.add(dog2);
        arrayList.add(cat);  // 不小心添加了一只猫
        // 遍历
        for (Object o :
                arrayList) {
            // 向下转型
            Dog dog = (Dog) o;
            System.out.println(dog.name);
        }
    }
}
class Dog{
    public String name;
    public Dog(String name) {
        this.name = name;
    }
}
class Cat{
    public String name;
    public Cat(String name) {
        this.name = name;
    }
}


🐱 传统方法可能遇到的问题:


不能对加入到集合的ArrayList中的数据类型进行约束(不安全);

遍历的时候,需要进行类型转换,在数据量大的情况下,对效率有影响。

而通过使用泛型,则可以很好的解决该类问题。

在创建 ArrayList 的时候,通过下面的语句使用泛型,限制元素的类型:


ArrayList<Dog> arrayList = new ArrayList<>();
1

则编译器就会对类型进行检查,避免了将Cat加入ArrayList的错误情况:


🐰 泛型的好处:


编译时,检查添加的元素类型,提高了安全性;

减少了类型转换的次数,提高了效率;

不再提示编译警告。

2 泛型讲解

2.1 泛型的介绍

🐱 简介:


泛型又称参数化类型,是jdk1.5出现的新特性,解决数据类型的安全性问题;

在类声明或实例化时只要指定好具体的类型即可;

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生 ClassCastException 异常。同时,代码更加简洁、健壮;

泛型的作用:在类声明时通过一个标识表示类中某个属性的类型,或者某个方法的返回值类型,或者参数类型。

class Person<E>{
    E element;  // E 表示 element 的数据类型,该数据类型在定义 Person 对象的时候指定,即在,编译期间,就确定E的类型
    public Person(E element){  // E 也可以是参数类型
        this.element = element;
    }
    public E method(){  // 返回类型为E
        return element;
    }
}

2.2 泛型实例

1.创建 3 个学生对象;

2.放入HashSet中,元素为Student;

3.通过遍历HashSet,将值放入到 HashMap 中,Key 为 String name(HashSet中的Student), Value 为学生对象;

4.使用两种方式遍历,HashMap。

import java.util.*;
/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 */
public class Generic2 {
    public static void main(String[] args) {
        HashSet<Student> students = new HashSet<>();
        students.add(new Student("黄小黄", 20));
        students.add(new Student("祢豆子", 7));
        students.add(new Student("春卷儿", 19));
        HashMap<String, Student> hashMap = new HashMap<>();
        // 遍历 HashSet,并将值传入 HashMap
        Iterator<Student> studentIterator = students.iterator();
        while (studentIterator.hasNext()){
            Student s = studentIterator.next();
            hashMap.put(s.name, s);
        }
        // 遍历 HashMap
        System.out.println("=================== 方式一 ==================");
        Set<String> keySet = hashMap.keySet();
        for (String s :
                keySet) {
            System.out.println(s + "--->" + hashMap.get(s));
        }
        System.out.println("=================== 方式二 ==================");
        Set<Map.Entry<String, Student>> entrySet = hashMap.entrySet();
        for (Map.Entry es :
                entrySet) {
            System.out.println(es.getKey() + "--->" + es.getValue());
        }
    }
}
// 学生类
class Student{
    public String name;
    public int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}



2.3 泛型使用细节

给泛型指定数据类型时,必须是引用类型,不能是基本数据类型;



给泛型指定具体类型后,可以传入该类型或者其子类类型;

泛型的使用形式如下:



如果不使用泛型,默认是使用 Object类型。

3 自定义泛型类

🆔 基本语法:

class 类名<T, R...>{
  ...
}

普通成员可以使用泛型(属性方法);

使用泛型的数组,不能初始化, 因为数组在 new 时不能确定泛型的类型,无法确定开辟的空间;

静态方法中不能使用类的泛型, 因为静态是和类相关的,在类加载的时候,对象还没创建,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化;

泛型类的类型,是在创建对象时确定的;

如果在创建对象时没有指定类型,默认为Object。

4 自定义泛型接口

🆔 基本语法:

interface 接口名<T, R...>{
  ...
}
  1. 接口中,静态成员也不能使用泛型;
  2. 泛型接口的类型,在继承接口或者实现接口的时候确定;
  3. 没有指定类型,依然为Object;
  4. 在 jdk8 中,可以在接口中使用默认方法,该方法可以使用泛型。

5 自定义泛型方法

🆔 基本语法:

修饰符 <T, R...>返回类型 方法名(参数列表){
  ...
}

泛型方法,可以定义在普通类中,也可以定义在泛型类中;


当泛型方法被调用时,类型就会确定;



public void f(E e){},修饰符后面没有 ,则 f 不是泛型方法,而是使用了泛型;


泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型。


6 泛型的继承与通配

泛型不具有继承性;


  1. :支持任意泛型类型;
  2. :支持A类以及A类的子类,规定了泛型的上限;
  3. :支持A类以及A类的父类,不限于直接父类,规定了泛型的下限。

🐰 通配示例如下,具体见注释:

import java.util.List;
/**
 * @author 兴趣使然黄小黄
 * @version 1.0
 */
public class Generic3 {
    // <?> 任意泛型
    public static void printCollection01(List<?> c){
        for (Object object:
             c) {
            System.out.println(object);
        }
    }
    // <? extends A> A表示上限,可以接受A或者A的子类
    public static void printCollection02(List<? extends A> c){
        for (Object object:
                c) {
            System.out.println(object);
        }
    }
    // <? super A> A表示下限,可以接受A以及A的父类
    public static void printCollection03(List<? super A> c){
        for (Object object:
                c) {
            System.out.println(object);
        }
    }
}
class C{}
class A extends C{}
class B extends A{}
相关文章
|
4月前
|
存储 Java
从零开始学习 Java:简单易懂的入门指南之可变参数及Collections类(二十五)
从零开始学习 Java:简单易懂的入门指南之可变参数及Collections类(二十五)
|
10月前
|
Java 程序员
【JavaSE】Java基础语法(二十):多态
1. 多态的概述 什么是多态 同一个对象,在不同时刻表现出来的不同形态 多态的前提 要有继承或实现关系 要有方法的重写 要有父类引用指向子类对象
|
10月前
|
安全 算法 Java
【JavaSE专栏19】谈谈泛型和枚举的那些事
【JavaSE专栏19】谈谈泛型和枚举的那些事
155 0
|
10月前
|
Java
【JavaSE】Java基础语法(十八):接口
1. 接口的概述 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。 Java中接口存在的两个意义 用来定义规范 用来做功能的拓展
|
10月前
|
Java
【JavaSE】Java基础语法(三十一):可变参数
1. 可变参数 可变参数介绍 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了 方法的参数类型已经确定,个数不确定,我们可以使用可变参数
|
10月前
|
Java
【JavaSE】Java基础语法(十六):抽象类
1. 抽象类的概述 当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了! 在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽 象类!
|
10月前
|
Java
【JavaSE】Java基础语法(二十二):包装类
1. 基本类型包装类 基本类型包装类的作用 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据常用的操作之一:用于基本数据类型与字符串之间的转换 基本类型对应的包装类
|
10月前
|
Java
【JavaSE】Java基础语法(十七)
1. final fianl关键字的作用 final代表最终的意思,可以修饰成员方法,成员变量,类 final修饰类、方法、变量的效果 fianl修饰类:该类不能被继承(不能有子类,但是可以有父类) final修饰方法:该方法不能被重写 final修饰变量:表明该变量是一个常量,不能再次赋值
|
12月前
|
Java
【JavaSE】Java基础语法(六):方法详解
【JavaSE】Java基础语法(六):方法详解
|
安全 Java 测试技术
java 泛型 万字详解(通俗易懂)
java 集合篇章——泛型 详解。
29986 2