从 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 子类。
相关推荐
爱学习的程序媛37 分钟前
Python常用框架梳理
开发语言·后端·python·ai·web·后端框架·python框架
GISer_Jing43 分钟前
Agent核心内容
后端·ai·langchain
budingxiaomoli2 小时前
Spring统一功能处理
java·后端·spring
某鹏3 小时前
网关选型:Go、Java、Graal 谁都不能完胜
后端
用户0247836530973 小时前
从屎山到防线:14 行状态迁移表,给任务系统装上防瞎跳闸门
后端
CQU_JIAKE3 小时前
11.13[体系结构】指标,五级流水线,八级流水线,虚实地址思考
后端
panbook5 小时前
从0到1实现一个分布式时序数据库(三):主从复制、故障检测与切换
后端
上线再说5 小时前
图片从 8.2MB 压到 296KB:我把小程序图片链路的账单拆了一遍
后端
dpharness5 小时前
我原以为 MCP 装完就能用,直到工具列表是空的
后端
dpharness5 小时前
153 个 MCP 服务,我把工具数一个个数了一遍
后端