自定义泛型类

本文深入讲解泛型类的定义和使用,包括自定义泛型类、泛型接口、泛型方法,以及泛型类的继承和子类实现。探讨了泛型在不同场景下的应用,如List泛型的应用,以及在实例化时如何指定泛型类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何自定义泛型结构:泛型类,泛型接口,泛型方法
泛型类和泛型接口的区别主要是类和接口的区别,所以就不去举泛型接口的例子
自定义泛型类:
以前我们定义的类的元素是非常确定的,但我们可能会涉及到这个类有一个属性,这个属性类型不太确定,可以给类加上泛型,泛型用大写字符表示:通常用T,E,K,V(K,V更多的运用在键值对的泛型中)
类的内部结构就可以使用类的泛型,下面的程序中把T称为类的泛型
接口中不能有构造器

//在实例化的时候指明T到底是什么类型
public class Order<T>{
    String orderName;
    int orderId;

    T orderT;
    public Order(){

    }
    public Order(String orderName,int orderId,T orderT){
        this.orderName=orderName;
        this.orderId=orderId;
        this.orderT=orderT;
    }

    public String getOrderName() {
        return orderName;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    public T getOrderT() {
        return orderT;
    }

    public void setOrderT(T orderT) {
        this.orderT = orderT;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderName='" + orderName + '\'' +
                ", orderId=" + orderId +
                ", orderT=" + orderT +
                '}';
    }
}

集合当中在List声明了泛型,ArrayList实现了它,这个泛型也保留了
Order是一个自定义的泛型类,在实际开发中如果需要提供Order的子类SubOrder,继承于Order
如果像这样进行了指明,造SubOrder的对象的时候,就不用加尖括号了(不用写成SubOrder<>了)

 class SubOrder extends Order<Integer>{
}

可以直接这么写
由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象时,不再需要指明泛型
这里注意Order是泛型类,subOrder不是泛型类,就是一个普通的类

class subOrder extends Order<Integer>{


}
public class SubOrder {

    public static void main(String[] args) {
        subOrder sub=new subOrder();
        sub.setOrderT(1233);//调用父类的方法,因为指明了Integer,所以此时要求传入的是Integer类型
    }
}

还有一种情况,继承Order但不去指明,此时SubOrder1是泛型类,实例化的时候可以指明类型

public class test01 {

    public static void main(String[] args) {
        SubOrder1<String> sub2=new SubOrder1<>();
        sub2.setOrderT("order2");//此时就要传入String类型
    }
}
class SubOrder1<T> extends Order<T>{


}

注意点:

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如: <E1,E2,E3>,有几个就放几个

  2. 泛型类的空参构造器如下:public GenericClass(){}。
    而下面是错误的:public GenericClass< E>(){},实例化的时候需要补上,此时new之后的尖括号里面可以为空,new SubOrder1<>();写成这样也可以:new SubOrder1< String>();

  3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。

  4. 泛型不同的引用不能相互赋值。 尽管在编译时ArrayList< String>和ArrayList< Integer>是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中

  5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。这里只是认为是Object但不等于是Object,因为在继承方面还是有区别。经验:泛型要使用就一直都用。要不用,就一直都不要用。

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

  7. jdk1.7,泛型的简化操作:ArrayList< String> list = new ArrayList<>();

  8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。

  9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型

public class Order<T>{
    String orderName;
    int orderId;

    T orderT;
    
    public static void show(){
        System.out.println(orderT);//会报错
    }
}

这么写也报错

    public static void show(T orderT){
        System.out.println(orderT);
    }

因为类的泛型是造对象的时候指定的,静态结构的加载早于对象的创建,所以是错误的
10. 异常类不能是泛型的,异常类不能声明为泛型类
不能这么写,编译不通过

    try{
        
    }catch(T t){
        
    }
public class MyException extends Exception{
//不报错
}
public class MyException<T> extends Exception{
//报错
}
  1. 不能使用new E[]。否则编译不通过,但是可以:E[] elements = (E[])new Object[capacity];
    public Order(){
        T[] arr=new T[10];
    }//编译不通过

可以这么写

    public Order(){
        T[] arr=(T[])new Object[10];
    }

new的其实还是Object类型的数组,但具体放数据的时候必须放T和其子类的对象,不要放Object对象,否则出类型转换异常,这样才能保证实际执行的时候不会出问题
12. 参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
13. 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留
部分保留
结论:子类除了指定或保留父类的泛型,还可以增加自己的泛型

class Father<T1, T2> {

        }
        // 子类不保留父类的泛型
        // 1)没有类型 擦除
         class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{ }

         }
         // 2)具体类型
         class Son2 extends Father<Integer, String> {

         }
         // 子类保留父类的泛型
        // 1)全部保留
         class Son3<T1, T2> extends Father<T1, T2> {

         }
         // 2)部分保留,Integer是确定的,另一个是不确定的
        class Son4<T2> extends Father<Integer, T2> {

        }
        class Father<T1, T2> {

        }
        // 子类不保留父类的泛型
        // 1)没有类型 擦除
         class Son<A, B> extends Father{//等价于class Son extends Father<Object,Object>{ }
				//这里是子类自己提供了两个泛型参数A,B,是额外的
         }
         // 2)具体类型
         class Son2<A, B> extends Father<Integer, String> {
				//如果父类中的方法用到了泛型,就是Integer和String,这里是自己额外又定义了两个泛型参数,在自己的东西中还可以用自己的A和B
         }
         // 子类保留父类的泛型
        // 1)全部保留
         class Son3<T1, T2, A, B> extends Father<T1, T2> {
				//在保留T1,T2的基础之上,又额外定义了两个
         }
         // 2)部分保留
        class Son4<T2, A, B> extends Father<Integer, T2> {
            //保留T2的同时自己又额外定义了两个
        }
<think>好的,用户想了解如何在Java、C#或Python中创建和使用自定义泛型类。首先,我需要回忆这三种语言中泛型的基本概念和语法。 对于Java,我记得泛型是在JDK 5引入的,使用尖括号<>来声明类型参。类名后面加上<T>这样的参。比如泛型类可以有一个或多个类型参,像List<T>那样。然后,用户需要知道如何实例化泛型类,比如new MyGenericClass<String>()。同时,可能需要提到类型擦除,Java在运行时不会保留泛型类型信息,这和C#不同。 然后是C#,泛型的语法类似,但实现方式不同。C#的泛型在运行时保留类型信息,所以效率可能更高,而且支持更多特性,比如泛型约束。创建泛型类时,同样使用<T>,实例化的时候需要指定具体类型,比如new MyGenericClass<string>()。可能还需要提到where关键字来添加约束。 至于Python,它本身没有严格的泛型类型系统,因为它是动态类型语言。但Python 3.5之后引入了类型提示,可以使用typing模块中的Generic和TypeVar来定义泛型类。不过实际运行时并不会强制类型检查,这更多是为了静态类型检查工具如mypy服务。用户可能需要了解如何使用TypeVar定义类型变量,并继承Generic来创建泛型类。 接下来,用户可能想知道如何在这些语言中实际编写代码示例。需要为每种语言提供一个简单的例子,比如一个泛型的Box类,存储某个类型的值。然后演示如何实例化和使用这个类。 还需要注意各个语言的差异,比如Java的类型擦除可能导致一些限制,而C#的泛型支持更多运行时操作。Python的例子可能更简单,但实际类型检查不如静态语言严格。 另外,用户提到的引用中,引用[1]提到泛型的参可以是通配符,比如Class<?>,这在Java中用于未知类型的泛型。引用[2]讨论了Java中类对象和类型的关系,可能在解释类型擦除时相关。引用[3]关于命名空间映射可能和C#的泛型关系不大,但可能在类型转换或命名约定中有间接联系,不过可能暂时不需要深入这部分。 需要确保回答结构清晰,分Java、C#、Python三个部分,每部分给出创建和使用的示例,并说明注意事项。同时,根据用户提供的引用,可能需要强调Java中的类型擦除和通配符使用,如引用[1]中的例子。 最后,生成相关问题需要考虑用户可能的后续疑问,比如性能差异、最佳实践、类型约束等。</think>### 自定义泛型类的创建与使用(Java/C#/Python) #### 1. Java 实现 在Java中,泛型通过**类型参**实现,语法为`<T>`。以下示例展示了一个存储任意类型值的泛型类`Box<T>`: ```java public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return this.content; } } ``` 使用方式: ```java Box<String> stringBox = new Box<>(); stringBox.setContent("Hello"); System.out.println(stringBox.getContent()); // 输出: Hello ``` **关键特性**: - 类型擦除:泛型信息在编译后会被擦除,运行时无法获取具体类型[^2] - 通配符类型:可使用`<?>`表示未知类型,如`List<?> list = new ArrayList<String>()`[^1] #### 2. C# 实现 C#使用`<T>`语法且支持运行时类型保留: ```csharp public class Box<T> { private T content; public void SetContent(T content) { this.content = content; } public T GetContent() { return this.content; } } ``` 使用方式: ```csharp var intBox = new Box<int>(); intBox.SetContent(42); Console.WriteLine(intBox.GetContent()); // 输出: 42 ``` **优势**: - 支持类型约束:`where T : IComparable` - 保留运行时类型信息 #### 3. Python 实现 Python通过`typing`模块实现类型提示(Python 3.5+): ```python from typing import TypeVar, Generic T = TypeVar('T') class Box(Generic[T]): def __init__(self): self.content = None def set_content(self, content: T) -> None: self.content = content def get_content(self) -> T: return self.content ``` 使用方式: ```python box = Box[str]() box.set_content("Dynamic Typing") print(box.get_content()) # 输出: Dynamic Typing ``` **注意**:实际运行时类型不会被强制检查,主要用于静态类型检查工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值