Java高级程序设计

泛型

泛型

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。... Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Visual Basic .NET 称之为泛型(generics);ML、Scala 和 Haskell 称之为参数多态(parametric polymorphism);C++ 和 D称之为模板。《Design Patterns》一书称之为参数化类型(parameterized type)。

https://zh.wikipedia.org/wiki/泛型

Java Generics

  • A way to control a class type definitions
  • A way of improving the clarity of code
  • A way of avoiding (casts) in code, turning run-time errors (typically ClassCastException) into compile-time errors.

葫芦娃中的例子

Position第一版

public class Position {
    private Object sth;
    public Position(Object sth) { 
        this.sth = sth; 
    }
    Object get() { 
        return sth; 
    }
}
Position里放任意东西

改进

Position第二版

public class Position {
    private Creature sth;
    public Position(Creature sth) { 
        this.sth = sth; 
    }
    Creature get() { 
        return sth; 
    }
}

通过构造方法和get方法我们能看出Position里放的是Creature

但如果是这样呢?

public class Position {
    private Creature sth;
}

从外部观察,何以了解Position是放Creature的?

用“泛型”显式说明一下

public class Position<T>{
    private T sth;
}

显式表达“某种不确定”: Position里是要放东西的,但不确定是什么东西

再明白一点

public class Position<T extends Creature>{
    private T sth;
}
显式表达Position是跟某种Creature有关系的

不用泛型的Stack

class Stack{
  void push(Object o){...}
  Object pop(){...}
}

String s = "Hello";
Stack st = new Stack();
...
st.push(s);
...
s = (String)st.pop();

用了泛型的Stack

class Stack<A>{ // 类参数
  void push(A a){...}
  A pop(){...}
}
String s = "Hello";
Stack<String> st = new Stack<String>();
st.push(s);
...
s = st.pop();

再定义一个Computer 

public class Computer{

    private HDD mHarddisk;   // 机械硬盘
    
    Computer(HDD harddisk){
        mHarddisk = harddisk;
    }
    public Data readDisk(){
        return mHarddisk.read();
    }
    public void writeDisk(Data data){
        mHarddisk.write(data);
    }
}
如果安装的是SSD怎么办?

定义一个SSDComputer ?

public class SSDComputer{

    private SSD mHarddisk;   // SSD硬盘

    Computer(SSD harddisk){
        mHarddisk = harddisk;
    }
    public Data readDisk(){
        return mHarddisk.read();
    }
    public void writeDisk(Data data){
        mHarddisk.write(data);
    }
}
这当然不好!

抽象一下

public abstract class Disk{};
public class SSD extends Disk{};
public class HHD extends Disk{};
public class Computer{
    private Disk disk;   // 抽象的硬盘
    Computer(Disk disk){
        this.disk = disk;
    }
    public Data readDisk(){ return disk.read(); }
    public void writeDisk(Data data){ disk.write(data); }
    public Disk getDisk(){ return disk; }
}
基于多态实现设计抽象(解耦)
但你还要cast

用泛型来做

public class Computer<T extends Disk>{
    private T disk;   // 参数类
    Computer(T disk){ this.disk = disk; }
    public Data readDisk(){ return disk.read(); }
    public void writeDisk(Data data){ disk.write(data); }
    public T getDisk(){ return disk; }
    public void setDisk(T disk){ this.disk = disk; }

    public static void main(String[] args) {
        Computer<SSD> computer = new Computer<SSD>(new SSD());
        SSD disk = computer.getDisk(); // No cast needed
        //computer.setDisk(new HHD()); // error!
    }
}
有了类型约束

泛型方法

public class GenericMethods {
    public <T> void f(T x) {
        System.out.println(x.getClass().getName());
    }
    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        gm.f("");
        gm.f(1); //autoboxing
        gm.f(1.0);
        gm.f(1.0F);
        gm.f(‘c’);
        gm.f(gm);
    }
}

再看容器

public class Holder<T> {
    private T obj;
    public void set(T obj){ this.obj = obj; }
    public T get(){ return obj; }
    public static void main(String[] args){
        Holder<Integer> holder = new Holder<>();
        holder.set(1);
        //holder.set("Abc"); // error
        Integer obj = holder.get(); //无须cast
    }       
}
多完美!可惜这只是编译时刻... 因为运行时的类型信息被擦掉了
javap -v -p -s -sysinfo -constants Holder.class

字节码中

Constant pool:
    #1 = Methodref          #9.#29         // java/lang/Object."<init>":()V
    #2 = Fieldref           #3.#30         // Holder.obj:Ljava/lang/Object;
    #3 = Class              #31            // Holder
    #4 = Methodref          #3.#29         // Holder."<init>":()V
    #5 = Methodref          #8.#32         // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    #6 = Methodref          #3.#33         // Holder.set:(Ljava/lang/Object;)V
    #7 = Methodref          #3.#34         // Holder.get:()Ljava/lang/Object;
    #8 = Class              #35            // java/lang/Integer
    #9 = Class              #36            // java/lang/Object
    ...
    #29 = NameAndType        #14:#15        // "<init>":()V
    #30 = NameAndType        #10:#11        // obj:Ljava/lang/Object;
    #31 = Utf8               Holder
    #32 = NameAndType        #37:#38        // valueOf:(I)Ljava/lang/Integer;
    #33 = NameAndType        #18:#19        // set:(Ljava/lang/Object;)V
    #34 = NameAndType        #21:#22        // get:()Ljava/lang/Object;

擦除

public class Computer<T extends Disk> {
    private T disk; // 运行时disk是Disk类型
    Computer(T disk) {
        this.disk = disk;
    }
    public void setDisk(T disk) {
        this.disk = disk;
    }
}
class Disk {};

Java泛型的实现方式是将类型参数用边界类型替换,在上面的例子中就是把T用Disk替换。这种实现方式看上去就像是把具体的类型擦除到了边界类型(父类Disk)。

字节码

Constant pool:
    #1 = Methodref          #4.#18         // java/lang/Object."<init>":()V
    #2 = Fieldref           #3.#19         // Computer.disk:LDisk;
    #3 = Class              #20            // Computer
    #14 = Utf8              setDisk
    #19 = NameAndType       #5:#6          // disk:LDisk;
    
    public void setDisk(T);
        descriptor: (LDisk;)V
        flags: (0x0001) ACC_PUBLIC
        Code:
        ...

这两个是不同类型么?

https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html

ArrayList<Integer> intList = new ArrayList<>();
ArrayList rawList = new ArrayList();

System.out.println(intList.getClass().getCanonicalName());
System.out.println(rawList.getClass().getCanonicalName());

Output:

java.util.ArrayList
java.util.ArrayList

擦除的后果

public class Holder<T> {
    private T obj;
    public void set(T obj){ this.obj = obj; }
    public T get(){ return obj; }
    public void testT(Object arg){
        if (arg instanceof T){ ... } //编译错误
        T var = new T(); //编译错误
        T[] array = new T[100]; //编译错误
        }
    }
}
这劳什子有何用?!

T存在的意义

public class Holder<T> {
    private T obj; //在编译时,该类中的所有的T都会被替换为边界类型Object。
    public void set(T obj){ this.obj = obj; }
    public T get(){ return obj; }
    public static void main(String[] args){
        Holder<Integer> holder = new Holder<>();
        //编译器会检查实参是不是一个Integer,
        //虽然这里的1是int类型,但是因为自动包装机制的存在,
        //他会被转化为一个Integer,因此能够通过类型检查。
        holder.set(1); 
        //编译器也会进行类型检查,
        //并且自动插入一个Object类型到Integer类型的转型操作。
        Integer obj = holder.get();
    }       
}

看看字节码

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #3                  // class Holder
         3: dup
         4: invokespecial #4                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: iconst_1
        10: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        13: invokevirtual #6                  // Method set:(Ljava/lang/Object;)V
        16: aload_1
        17: invokevirtual #7                  // Method get:()Ljava/lang/Object;
        20: checkcast     #8                  // class java/lang/Integer
        23: astore_2
        24: return

泛型的实际实现

  • 对泛型的处理全部集中在编译期,在编译时,编译器会执行如下操作。
    • 会将泛型类的类型参数都用边界类型替换。
    • 对于传入对象给方法形参的指令,编译器会执行一个类型检查,看传入的对象是不是类型参数所指定的类型。
    • 对于返回类型参数表示对象的指令,也会执行一个类型检查,还会插入一个自动的向下转型,将对象从边界类型向下转型到类型参数所表示的类型。

这些都是错的 

public class Holder<T> {
    private T obj;
    public void set(T obj){ this.obj = obj; }
    public T get(){ return obj; }
    public void testT(Object arg){
        if (arg instanceof T){ ... } //编译错误
        T var = new T(); //编译错误
        T[] array = new T[100]; //编译错误
        }
    }
}

如果真想生成泛型对象?

class Holder<T>{
    private T t;
    public void init(IFactory<T> factory){
        this.t = factory.create();  // 此处即为new T()的工厂方法的实现
    }
}
interface IFactory<T>{  //接口也可以参数化
    T create();
}
class IntegerFactory implements IFactory<Integer>{
    public Integer create(){
        return new Integer(10);
    }
}
public class newTwithFactory{
    public static void main(String[] args){
        Holder<Integer> holder = new Holder<>();
        holder.init(new IntegerFactory());
    }
}

或者可以使用RTTI

class Holder<T>{
    private T t;
    private Class<T> kind;
    public Holder(Class<T> kind){ this.kind = kind; }
    public void init(){
        try{
            this.t = kind.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Holder<Integer> holder = new Holder<>(Integer.class);
        holder.init();
    }
}

边界 Bounds

extends申明对参数类型的限制条件

interface HasColor{ java.awt.Color getColor(); }

class Colored <T extends HasColor>{ ... }

class Dimension { public int x,y,z; }

class ColoredDimension <T extends HasColor & Dimension>{ ... } //错误!
class ColoredDimension <T extends Dimension & HasColor>{ ... }//正确!类必须在前,接口在后
    

泛型的意义:类型安全(Type Safety)

// 没有泛型 - 容易出错
List list = new ArrayList();
list.add("Hello");
list.add(123);
String str = (String) list.get(0); // 需要强制类型转换
Integer num = (Integer) list.get(1); // 运行时可能出错


// 有泛型 - 编译时类型检查
List<String> list = new ArrayList<>();
list.add("Hello");
list.add(123); // 编译错误!类型不匹配
String str = list.get(0); // 无需强制转换,类型安全

泛型的意义:消除强制类型转换

// 没有泛型
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // 需要强制转换

// 有泛型
List<String> list = new ArrayList<>();
list.add("Hello");
String str = list.get(0); // 自动类型推断,无需转换

泛型的意义:提高代码可读性

// 没有泛型 - 不清楚存储什么类型
Map userCache = new HashMap();
userCache.put("user1", new User("张三"));

// 有泛型 - 一目了然
Map<String, User> userCache = new HashMap<>();
userCache.put("user1", new User("张三"));

泛型的意义:代码复用

// 没有泛型 - 需要为每种类型写重复代码
public class StringBox/IntegerBox {
    private String/Integer value;
    public String/Integer get() { return value; }
    public void set(String/Integer value) { this.value = value; }
}

// 有泛型 - 一个类处理所有类型
public class Box<T> {
    private T value;
    public T get() { return value; }
    public void set(T value) { this.value = value; }
}

泛型的意义:算法通用性

// 泛型排序算法
public static <T extends Comparable<T>> void sort(List<T> list) {
    // 可以对任何实现了Comparable接口的类型进行排序
    Collections.sort(list);
}

// 使用示例
List<String> strings = Arrays.asList("c", "a", "b");
List<Integer> numbers = Arrays.asList(3, 1, 2);
sort(strings);  // 排序字符串
sort(numbers);  // 排序数字

实际项目中的应用

//数据访问层
public interface Repository<T, ID> {
    T findById(ID id);
    List<T> findAll();
    T save(T entity);
    void deleteById(ID id);
}

//事件系统
public interface EventHandler<T extends Event> {
    void handle(T event);
    Class<T> getEventType();
}

泛型的核心价值

  • 类型安全:编译时发现类型错误,避免运行时异常
  • 代码简洁:消除强制类型转换,代码更清晰
  • 代码复用:一个泛型类可以处理多种类型
  • 性能提升:避免装箱拆箱,减少类型转换开销
  • 维护性:代码更易读、易维护、易扩展
  • 设计灵活性:支持复杂的设计模式和架构

看看这个例子


class Fruit{}
class Apple extends Fruit{}

public class NonConvariantGeneric {
    List<Fruit> flist = new ArrayList<Apple>(); //编译错误
}

Apple的List不是Fruit的List。Apple的List将持有Apple和Apple的子类型,Fruit的List将持有任何类型的Fruit。是的,这包括Apple,但是它不是一个Apple的List,它仍然是Fruit的List。Apple的List在类型上不等价于Fruit的List,即使Apple是一种Fruit类型。

再看看这个例子

class Fruit {}
class Apple extends Fruit {}

class Plate<T>{
    private T item;
    public Plate(T t){item=t;}
    public void set(T t){item=t;}
    public T get(){return item;}
}

//现在我定义一个“水果盘子”,逻辑上水果盘子当然可以装苹果。
//但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。
Plate<Fruit> p = new Plate<Apple>(new Apple()); //编译错误!
“苹果” IS-A “水果”, BUT “装苹果的盘子” NOT-IS-A “装水果的盘子”!

通配符

class Fruit{}
class Apple extends Fruit{}
public class GenericsAndCovariance {
    public static void main(String[] args){
        //一个能放水果以及一切是水果派生类的盘子,啥水果都能放的盘子
        //Plate<? extends Fruit>和Plate<Apple>最大的区别就是:
        //Plate<? extends Fruit>是Plate<Fruit>以及Plate<Apple>的基类。
        Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
        // a list of any type that's inherited from Fruit
        List<? extends Fruit> flist = new ArrayList<Apple>();
    }
}

扩展一下

class Food{}
//Lev 2
class Fruit extends Food{}
class Meat extends Food{}
//Lev 3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork extends Meat{}
class Beef extends Meat{}
//Lev 4
class RedApple extends Apple{}
class GreenApple extends Apple{}

https://www.zhihu.com/question/20400700

通配

but

class Fruit{}
class Apple extends Fruit{}

       Plate<? extends Fruit> p=new Plate<Apple>(new Apple());
        //不能存入任何元素
        p.set(new Fruit());    //Error
        p.set(new Apple());    //Error
        //读取出来的东西只能存放在Fruit或它的基类里。
        Fruit newFruit1=p.get();
        Object newFruit2=p.get();
        Apple newFruit3=p.get();    //Error
"A Plate of any type that's inherited from Fruit" 的意思不是"A Plate will hold any type of Fruit" but means "some specific type which is not specified"

super

表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子。

Plate<?super Fruit>覆盖图中红色的区域。

but

class Fruit{}
class Apple extends Fruit{}
public class GenericsAndCovariance {
    public static void main(String[] args){
       Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());
        //存入元素正常
        p.set(new Fruit());
        p.set(new Apple());
        //读取出来的东西只能存放在Object类里。
        Apple newFruit3=p.get();    //Error
        Fruit newFruit1=p.get();    //Error
        Object newFruit2=p.get();
    }
}

区别?

///无界通配符 Unbounded Wildcards
List<?>
List
List<Object>

Overloading

public class UseList<W,T>{
    void f(List<T> v){}
    void f(List<W> v){}
}
NO!

Self-bounded types 😢

class SelfBounded<T extends SelfBounded<T>>{
    ...
}

https://stackoverflow.com/questions/19588449/self-bounded-generics

https://jishu.dev/2021/03/03/java-self-bounded-types/

从简单的开始

public class BasicHolder<T> {
    T element;
    void set(T arg){this.element = arg;}
    T get(){return this.element;}
    void print(){
        System.out.println(element.getClass().getSimpleName());
    }
}
public class SubType extends BasicHolder<SubType> {
    public static void main(String[] args){
        SubType s1 = new SubType();
        SubType s3 = s1.get();
        s1.print();
        s3.print();
    }
}

得到什么?

public class SubType extends BasicHolder<SubType> {}
public class Plate extends BasicHolder<Plate>{}
...

... the generic base class becomes a kind of template for comon functionality for all its derived class


跟实现一个父类有什么区别?

跟普通继承关系的区别

... but this functionality will use the derived type for all of its arguments and return values ...


漂亮!

还有一点小问题

class Other{}
class BasicOther extends BasicHolder<Other>{}

public static void main(String[] args){
    BasicOther b = new BasicOther(), b2 = new BasicOther();
    b.set(new Other());
    Other other = b.get();
    b.print();// Other
}

没完全限制

Self-bounded

//forcing the generic to be used as its own bound argument
class SelfBounded<T extends SelfBounded<T>> {
    T element;
    SelfBounded<T> set(T arg){
        element = arg;
        return this;
    }
    T get(){return element;}
}

class A extends SelfBounded<A> {}
class B extends SelfBounded<A> {} //ok 
class D {}
class E extends SelfBounded<D>{} //error

public static void main(String[] args){
        A a = new A();
        a.set(new A());
        a.print();
        B b = new B(), a2 = new B();
        //b.set(b2); //Error
        //b.print();
    }

高大上一点的说法





Argument covariance
&
Covariant return types

协变与逆变

协变与逆变(Covariance and contravariance )是在计算机科学中,描述具有父/子型别关系的多个型别通过型别构造器、构造出的多个复杂型别之间是否有父/子型别关系的用语。

https://zh.wikipedia.org/wiki/协变与逆变

Java 泛型 vs C++ 模板

Java 泛型和 C++ 模板虽然语法相似,但实现机制完全不同。

特性 Java 泛型 C++ 模板
实现机制 类型擦除 代码生成
编译结果 单一字节码 每个类型生成独立代码
运行时类型信息 丢失 保留
性能 装箱开销(基本类型) 无装箱,完全优化
特化 不支持 支持模板特化

Java 泛型:类型擦除

// 源代码
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();

// 编译后(字节码)
List strings = new ArrayList();  // 类型信息被擦除
List integers = new ArrayList(); // 同样的字节码

// 运行时
System.out.println(strings.getClass() == integers.getClass()); // true

再次强调:泛型只存在于编译期,用于类型检查

C++ 模板:代码生成

// 源代码
template<typename T>
class Container {
    T value;
public:
    void set(T v) { value = v; }
    T get() { return value; }
};
// 编译后:生成了3份完全不同的代码!
Container<int> intContainer;      // 生成 Container_int 类
Container<double> doubleContainer; // 生成 Container_double 类
Container<string> strContainer;    // 生成 Container_string 类

每个类型实例化生成独立代码,运行时类型信息完全保留

性能对比

Java 泛型:装箱和拆箱有性能开销

List<Integer> list = new ArrayList<>();
list.add(42);        // 装箱:int → Integer
int value = list.get(0); // 拆箱:Integer → int

C++ 模板:编译器生成针对 int 的优化代码

std::vector<int> vec;
vec.push_back(42);   // 直接存储 int,无开销
int value = vec[0];  // 直接访问,无开销

C++ 模板的编译时能力

C++ 模板支持编译时计算元编程

template<int N>
struct Factorial {
    static const int value = N * Factorial<N-1>::value;
};

template<>
struct Factorial<0> {
    static const int value = 1;
};

// 编译时计算阶乘,编译时就计算出结果!
int result = Factorial<5>::value;  // 120,编译期就是常量

Kokkos

一个可移植计算框架,通过模板参数在编译时选择不同的硬件后端。

template<class ExecutionSpace>
void compute() {
    Kokkos::parallel_for(
        Kokkos::RangePolicy<ExecutionSpace>(0, N),
        KOKKOS_LAMBDA(int i) { data[i] = data[i] * 2; // 计算代码 }
    );
}
// 同一份代码,编译时根据 ExecutionSpace 生成不同代码
compute<Kokkos::Cuda>();      // → CUDA GPU 代码
compute<Kokkos::OpenMP>();    // → OpenMP 多线程代码
compute<Kokkos::Serial>();    // → 串行 CPU 代码

Kokkos 的编译时代码生成

// ExecutionSpace 是编译时参数
template<class ExecutionSpace = Kokkos::DefaultExecutionSpace>
class MyKernel {
public:
    void run(int n) {
        Kokkos::parallel_for(
            Kokkos::RangePolicy<ExecutionSpace>(0, n),
            *this
        );
    }
    
    KOKKOS_INLINE_FUNCTION
    void operator()(int i) const {
        // 这段代码会根据 ExecutionSpace 生成:
        // - CUDA 设备代码(GPU)
        // - OpenMP 并行代码(多核 CPU)
        // - 或串行代码(单核 CPU)
        result[i] = compute(data[i]);
    }
};

编译时平台选择

// 编译时通过模板参数选择执行策略,同一份代码编译出3个不同版本的二进制文件!
#if defined(KOKKOS_ENABLE_CUDA)
    using ExecutionSpace = Kokkos::Cuda;
    using MemorySpace = Kokkos::CudaSpace;
#elif defined(KOKKOS_ENABLE_OPENMP)
    using ExecutionSpace = Kokkos::OpenMP;
    using MemorySpace = Kokkos::HostSpace;
#else
    using ExecutionSpace = Kokkos::Serial;
    using MemorySpace = Kokkos::HostSpace;
#endif

Kokkos::View<double*, MemorySpace> data("data", N);
Kokkos::parallel_for(
    Kokkos::RangePolicy<ExecutionSpace>(0, N),
    KOKKOS_LAMBDA(int i) { data[i] = i * 2.0; }
);
// myapp_cuda    - GPU 版本
// myapp_openmp  - 多线程版本
// myapp_serial  - 串行版本

Kokkos 的类型系统

template<class MemorySpace>
class View {
    // MemorySpace 可以是:
    // - Kokkos::CudaSpace         → GPU 显存
    // - Kokkos::CudaUVMSpace      → 统一虚拟内存
    // - Kokkos::HostSpace         → CPU 内存
    // - Kokkos::CudaHostPinnedSpace → 锁页内存
};
// 编译器根据模板参数生成不同的内存访问代码
Kokkos::View<double*, Kokkos::CudaSpace> gpu_data;  // GPU 访问
Kokkos::View<double*, Kokkos::HostSpace> cpu_data;  // CPU 访问

// 编译时类型检查
gpu_data = cpu_data;  // 编译错误!不同内存空间

Kotlin 的解决方案:inline + reified

Kotlin 作为运行在 JVM 上的语言,也面临类型擦除问题,但提供了 reified 关键字(与inline配合)来保留运行时类型信息。

// public <T> void printType() {
//     System.out.println(T.class);  // Java 无法实现,编译错误
// }

inline fun <reified T> printType() {
    println(T::class.java)  // Kotlin可以运行时获取类型
}

// 使用
printType<String>()  // 输出:class java.lang.String
printType<Int>()     // 输出:class java.lang.Integer

reified 的工作原理

inline 函数会在调用处展开,泛型类型被具体类型替换

inline fun <reified T> createInstance(): T {
    return T::class.java.newInstance() as T
}

val str: String = createInstance<String>()
val list: ArrayList<Int> = createInstance<ArrayList<Int>>()

编译后(Java示意代码)

String str = (String) String.class.newInstance();
ArrayList list = (ArrayList) ArrayList.class.newInstance();

Kotlin 的“泛型”

inline fun <reified T> isInstance(obj: Any): Boolean {
    return obj is T  // 可以使用 T 进行类型检查
}

// 调用
isInstance<String>("hello")  // true
isInstance<Int>("hello")     // false

reified 的实际应用

Android 中的常见场景

// Java 的繁琐写法
Intent intent = new Intent(context, TargetActivity.class);
startActivity(intent);

// Kotlin + reified 的优雅写法
inline fun <reified T : Activity> Context.startActivity() {
    startActivity(Intent(this, T::class.java))
}

// 使用
context.startActivity<TargetActivity>()  // 简洁!

三种泛型实现对比

特性 Java 泛型 Kotlin reified C++ 模板
运行时类型信息 擦除 有限支持 完整保留
实现方式 类型擦除 内联展开 代码生成
性能开销 装箱/拆箱 无(内联)
代码大小 增大(内联) 增大(多份代码)
使用限制 无特殊限制 必须 inline 编译时间长
类型安全 编译期 编译期 编译期
元编程

最终对比总结

Java 泛型:平衡了安全性和兼容性,适合大型业务应用

Kotlin reified

  • 巧妙绕过 JVM 限制,提升开发体验
  • 适合 Android 和现代 JVM 应用

C++ 模板

  • 终极性能和灵活性,适合系统级编程和高性能计算
  • Kokkos 等框架的基石