从 TypeScript 视角读懂 Java 和 TS 类中 new 自己的区别

Java class 里 new 自身 和 TypeScript 里 new 自己的区别分析

在日常开发中,我们经常会在类的内部通过 new 关键字实例化自身对象。Java 和 TypeScript 都支持面向对象编程,但它们在"类内 new 自身"这一行为上有着本质的区别。本文将从语法、运行时行为、类型系统等角度,详细分析 Java 和 TypeScript 在这方面的异同。


一、Java 中 class 里 new 自身

Java 是强类型、编译型语言。类内部 new 自身是非常常见的写法,比如:

java 复制代码
public class Person {
    public Person() {
        System.out.println("Person 构造方法被调用");
    }

    public void createAnother() {
        Person p = new Person();
        System.out.println("又创建了一个 Person");
    }
}

特点与机制:

  1. 类型确定
    在类内部,new Person() 总是创建当前类的实例,类型就是 Person。如果该类被继承,子类内部 new Person() 依然是父类对象,不是子类。
  2. 构造方法调用
    会调用当前类的构造方法,构造链严格按照声明顺序执行。
  3. 用途
    常用于工厂方法、原型模式、递归结构等。
  4. 限制
    不能直接通过 this 关键字 new 自身(new this() 不合法)。只能通过类名 new。

二、TypeScript 中 class 里 new 自己

TypeScript 作为 JavaScript 的超集,支持类的语法糖。来看一个例子:

typescript 复制代码
class Person {
    constructor() {
        console.log("Person 构造函数被调用");
    }

    createAnother() {
        const p = new Person();
        console.log("又创建了一个 Person");
    }
}

特点与机制:

  1. 类型推断
    new Person() 创建的是当前类的实例,类型为 Person。但 TypeScript 支持更灵活的 this 类型(多态 this),可以实现"new this()"的效果。

  2. 多态 this

    TypeScript 支持 new this.constructor()new (this.constructor as any)(),可以在父类方法中创建子类实例。还可以用 this 类型返回当前实例的实际类型。

    例如:

    typescript 复制代码
    class Base {
        clone(): this {
            return new (this.constructor as any)();
        }
    }
    
    class Sub extends Base {}
    
    const s = new Sub();
    const s2 = s.clone(); // s2 的类型是 Sub
  3. 用途

    常用于工厂方法、链式调用、流式 API 等场景。

  4. 灵活性

    TypeScript 允许通过 this 类型实现更灵活的"new 自己",支持继承链上的多态。

三、核心区别对比

维度 Java TypeScript
语法 只能 new 类名() 可以 new 类名(),也可以 new (this.constructor as any)()
类型 new 的总是当前类类型 支持 this 类型,多态更强
继承下行为 子类 new 父类名,得到父类对象 子类可通过 this.constructor 得到子类对象
运行时机制 编译期类型固定,运行时无多态 new 运行时可动态 new 当前实例的构造函数
典型应用 工厂方法、原型模式 工厂方法、链式调用、流式 API

四、TS 中 constructor 的访问修饰符

TypeScript 中,constructor 默认是 public,所以在类的外部和内部都可以通过 new 关键字来实例化对象。

typescript 复制代码
class Person {
  constructor() {
    console.log("Person 构造函数被调用");
  }
}

const p = new Person(); // 合法

如果你将 constructor 显式声明为 private 或 protected,则只能在类的内部(或子类内部,protected 情况下)通过 new 创建实例,外部无法 new。例如:

typescript 复制代码
class Person {
  private constructor() {}

  static getInstance() {
    return new Person();
  }
}

const p = new Person(); // 报错:构造函数为私有
const p2 = Person.getInstance(); // 合法

protected 构造函数允许子类继承和实例化,但禁止外部直接 new 父类:

typescript 复制代码
class Base {
  protected constructor() {}
}

class Sub extends Base {
  constructor() {
    super();
  }
}

const b = new Base(); // 报错
const s = new Sub();  // 合法

五、Java 中的构造器访问控制

Java 也有类似的访问修饰符(public、protected、private),但和 TypeScript 有细微差别:

  • public:任何地方都能 new
  • protected:同包或子类能 new
  • private:只能在类内部 new

例如:

java 复制代码
public class Singleton {
    private Singleton() {}
    public static Singleton getInstance() {
        return new Singleton();
    }
}

和 TypeScript 的单例写法如出一辙。

六、常见面试题与陷阱

1. Java 子类能否在父类方法中 new this?

不能。Java 的 this 代表当前实例,不能直接用 new this()。如果想在父类方法中创建"当前实际类型"的对象,通常需要借助反射:

java 复制代码
public class Base {
    public Base create() throws Exception {
        return this.getClass().getDeclaredConstructor().newInstance();
    }
}

但这种写法有一定的复杂性和性能开销。

2. TypeScript 的 this 类型陷阱

虽然 TS 支持 this 类型,但如果构造函数有参数,this.constructor as any 的类型检查就会失效,容易出错:

typescript 复制代码
class A {
  constructor(public name: string) {}
  clone(): this {
    // 这里需要传递参数,否则报错
    return new (this.constructor as any)(this.name);
  }
}

七、总结与最佳实践

  • Java:类内 new 自身只能 new 明确的类名,继承时不会多态。需要多态时可用反射或工厂模式。
  • TypeScript:类内 new 自身可以用 this.constructor 实现多态,配合 this 类型更灵活。constructor 默认 public,访问控制可实现单例等模式。

建议:

  • 需要多态工厂时,TypeScript 推荐用 this 类型+静态工厂方法。
  • Java 推荐用工厂方法或反射,避免直接在父类中 new 子类。
相关推荐
起这个名字3 小时前
Langchain4j Rag 知识库教程
java·后端
钟离墨笺3 小时前
Go语言-->Goroutine 详细解释
开发语言·后端·golang
用户68545375977693 小时前
🧪 设计一个全链路压测系统:战前的演习!
后端
Yeats_Liao4 小时前
Go Web 编程快速入门 11 - WebSocket实时通信:实时消息推送和双向通信
前端·后端·websocket·golang
R.lin4 小时前
使用注解将日志存入Elasticsearch
java·大数据·后端·elasticsearch·中间件
用户0806765692534 小时前
蓝桥云课-罗勇军算法精讲课(Python版)视频教程
后端
用户0806765692534 小时前
C#.NET高级班进阶VIP课程
后端
用户401426695854 小时前
Pandas数据分析实战(完结)
后端
用户84298142418104 小时前
js中如何隐藏eval关键字?
前端·javascript·后端