【Java】—— 泛型:自定义泛型类、泛型方法

目录

3. 自定义泛型结构

3.1 泛型的基础说明

3.2 自定义泛型类或泛型接口

3.2.1 说明

3.2.2 注意

3.2.2 举例

3.2.3 练习

3.3 自定义泛型方法

3.3.1 说明

3.3.2 举例

3.3.3 练习


3. 自定义泛型结构

3.1 泛型的基础说明

1、<类型>这种语法形式就叫泛型。

  • <类型>的形式我们称为类型参数,这里的"类型"习惯上使用T表示,是Type的缩写。即:

  • :代表未知的数据类型,我们可以指定为等。

    • 类比方法的参数的概念,我们把,称为类型形参,将称为类型实参,有助于我们理解泛型

  • 这里的T,可以替换成K,V等任意字母。

2、在哪里可以声明类型变量

  • 声明类或接口时,在类名或接口名后面声明泛型类型,我们把这样的类或接口称为泛型类泛型接口

【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 接口们】{
    
}
【修饰符】 interface 接口名<类型变量列表> 【implements 接口们】{
    
}

//例如:
public class ArrayList    
public interface Map{
    ....
}    

  • 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明了类型变量的方法,称为泛型方法。

[修饰符] <类型变量列表> 返回值类型 方法名([形参列表])[throws 异常列表]{
    //...
}

//例如:java.util.Arrays类中的
public static List asList(T... a){
    ....
}

3.2 自定义泛型类或泛型接口

        当我们在类或接口中定义某个成员时,该成员的相关类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么我们可以使用泛型类、泛型接口。

3.2.1 说明

① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。

② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。

③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。

  • 经验:泛型要使用一路都用。要不用,一路都不要用。

④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。

⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。

如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。

我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。

3.2.2 注意

① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:

② JDK7.0 开始,泛型的简化操作:ArrayList flist = new ArrayList<>();

③ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。

④ 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];

参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

⑤ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。

⑥ 异常类不能是带泛型的。

3.2.2 举例

举例1:

class Person {
    // 使用T类型定义变量
    private T info;
    // 使用T类型定义一般方法
    public T getInfo() {
        return info;
    }
    public void setInfo(T info) {
        this.info = info;
    }
    // 使用T类型定义构造器
    public Person() {
    }
    public Person(T info) {
        this.info = info;
    }
    // static的方法中不能声明泛型
    //public static void show(T t) {
    //
    //}
    // 不能在try-catch中使用泛型定义
    //public void test() {
        //try {
        //
        //} catch (MyException ex) {
        //
        //}
    //}
}

举例2:

class Father {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son1 extends Father {// 等价于class Son extends Father{
}
// 2)具体类型
class Son2 extends Father {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3 extends Father {
}
// 2)部分保留
class Son4 extends Father {
}

举例3:

class Father {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son extends Father{//等价于class Son extends Father{
}
// 2)具体类型
class Son2 extends Father {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3 extends Father {
}
// 2)部分保留
class Son4 extends Father {
}
3.2.3 练习

定义个泛型类 DAO,在其中定义一个Map 成员变量,Map 的键为 String 类型,值为 T 类型。

分别创建以下方法:
public void save(String id,T entity): 保存 T 类型的对象到 Map 成员变量中
public T get(String id):从 map 中获取 id 对应的对象
public void update(String id,T entity):替换 map 中key为id的内容,改为 entity 对象
public List list():返回 map 中存放的所有 T 对象
public void delete(String id):删除指定 id 对象

定义一个 User 类:
该类包含:private成员变量(int类型) id,age;(String 类型)name。

定义一个测试类:
创建 DAO 类的对象, 分别调用其 save、get、update、list、delete 方法来操作 User 对象,
使用 Junit 单元测试类进行测试。

DAO.java

package exer2;

import java.util.*;

/**
 * ClassName:IntelliJ IDEA
 * Description:
 * 定义个泛型类 DAO,在其中定义一个Map 成员变量,Map 的键为 String 类型,值为 T 类型。
 * 

* 分别创建以下方法: * public void save(String id,T entity): 保存 T 类型的对象到 Map 成员变量中 * public T get(String id):从 map 中获取 id 对应的对象 * public void update(String id,T entity):替换 map 中key为id的内容,改为 entity 对象 * public List list():返回 map 中存放的所有 T 对象 * public void delete(String id):删除指定 id 对象 * * @Author zyjstart * @Create:2024/10/5 22:48 */ public class DAO { Map map ; { map = new HashMap<>(); } public void save(String id,T entity){ //保存 T 类型的对象到 Map 成员变量中 if (!map.containsKey(id)){ // 不包含该id的时候进行添加,如果添加前包含该id,则变成了替换了 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 对象 if (map.containsKey(id)){ // 包含该key时,进行替换 map.put(id,entity); } } public List list(){ // 返回 map 中存放的所有 T 对象 Collection values = map.values(); ArrayList list = new ArrayList<>(); list.addAll(values); return list; } public void delete(String id){ // 删除指定 id 对象 map.remove(id); } }

User.java

package exer2;

import java.util.Objects;

/**
 * ClassName:IntelliJ IDEA
 * Description:
 *      定义一个 User 类:
 *      该类包含:private成员变量(int类型) id,age;(String 类型)name。
 * @Author zyjstart
 * @Create:2024/10/5 23:10
 */
public class User {
    private int id;
    private int age;
    private String name;

    public User() {
    }

    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id &&
                age == user.age &&
                Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, age, name);
    }
}

DAOTest.java

package exer2;

import java.util.List;

/**
 * ClassName:IntelliJ IDEA
 * Description:
 *      定义一个测试类:
 *      创建 DAO 类的对象, 分别调用其 save、get、update、list、delete 方法来操作 User 对象,
 *      使用 Junit 单元测试类进行测试。
 * @Author zyjstart
 * @Create:2024/10/5 23:11
 */
public class DAOTest {
    public static void main(String[] args) {
        DAO dao = new DAO<>();

        //添加
        dao.save("1001",new User(1,23,"流的话"));
        dao.save("1002",new User(2,23,"力道山"));

        //修改
        dao.update("1002",new User(3,45,"陈亚玲"));

        // 删除
        dao.delete("1002");

        List list = dao.list();
        for (User user : list) {
            System.out.println(user);
        }
    }
}

 

3.3 自定义泛型方法

        如果我们定义类、接口时没有使用<泛型参数>,但是某个方法形参类型不确定时,这个方法可以单独定义<泛型参数>。

3.3.1 说明
  • 泛型方法的格式:

[访问权限]  <泛型>  返回值类型  方法名([泛型标识 参数名称])  [抛出的异常]{
    
}
  • 方法,也可以被泛型化,与其所在的类是否是泛型类没有关系。

  • 泛型方法中的泛型参数在方法被调用时确定。

  • 泛型方法可以根据需要,声明为static的。

3.3.2 举例

举例1:

public class DAO {

    public  E get(int id, E e) {

        E result = null;

        return result;
    }
}

举例2:

public static  void fromArrayToCollection(T[] a, Collection c) {
    for (T o : a) {
        c.add(o);
    }
}

public static void main(String[] args) {
    Object[] ao = new Object[100];
    Collection co = new ArrayList();
    fromArrayToCollection(ao, co);

    String[] sa = new String[20];
    Collection cs = new ArrayList<>();
    fromArrayToCollection(sa, cs);

    Collection cd = new ArrayList<>();
    // 下面代码中T是Double类,但sa是String类型,编译错误。
    // fromArrayToCollection(sa, cd);
    // 下面代码中T是Object类型,sa是String类型,可以赋值成功。
    fromArrayToCollection(sa, co);
} 
  
3.3.3 练习

练习1: 泛型方法

编写一个泛型方法,实现任意引用类型数组指定位置元素交换。

public static void method1( E[] e,int a,int b)

public class Exer01 {

    //编写一个泛型方法,实现任意引用类型数组指定位置元素交换。
    public static  void method( E[] arr,int a,int b){
        E temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    @Test
    public void testMethod(){
        Integer[] arr = new Integer[]{10,20,30,40};
        method(arr,2,3);

        for(Integer i : arr){
            System.out.println(i);
        }
    }
}

你可能感兴趣的:(JAVA,java,开发语言)