白猫のメモ帳

C#とかJavaとかJavaScriptとかHTMLとか機械学習とか。

JavaとC#とTypeScriptのinterfaceの違い

こんにちは。
急に気候が変わってついていけません。
窓を開けると湿度の低さにびっくりします。

さて、最近たまに記事に書いているように、ちょくちょくTypeScriptを触っています。
もともとJavaをやっていて、いまはC#をメイン言語にしているので文法的にそんなに違和感はないのですが、ときどき「ん?」となることがあるので自分用のメモがてら書き残してみます。

interfaceのちょっとした違い

普段からオブジェクト指向言語を触っているのでclassとinterfaceについては特に問題はないのですが、微妙に機能が違うので混乱します。
interfaceはざっくりいうとclassなどが実装すべきメソッドなど定義した型です。定義しているだけなので実体はありません。
外から見えるためのインタフェース(ここでの意味はもう少し一般的な意味)なのですべてはpublicです。(つまり外に見せないものを書くものではない)

Java
interface Hoge1 {
  public abstract String method1();
  public static final String CONST1 = "Fuga";
}

Javaの場合は基本的には抽象メソッドと定数を書くことができます。
アクセス修飾子とかabstractとかは省略もできますし、書くこともできます。たぶん好み。
全部略すとメソッドは暗黙的にpublic abstractに、定数はpublic static finalになります。

基本的にと書いたのはだんだんできることが増えてるから。
たしか1.8からstaticメソッドの定義とデフォルト実装ができるようになっています。
(1.8から追ってないのでもっとなんかできるようになってたりするのかも)

interface Hoge2 {
  public String method1();
  public static String method2() {
    return "hoge";
  }
  public default String method3() {
    return "hogehoge";
  }
}

継承したりしたらどうなるかはややこしいのでここでは触れません。
定数が書けるので定義的なものはinterfaceに書くことが多かったような気がします。

継承と実装は区別されており、前者はextends、後者はimplementsを使います。

class Fuga implements Hoge1 {
  @Override
  public String method1() {
    return "hoghogehoge";
  }
}
interface HogeHoge extends Hoge1 {
  public String method4();
}

ちなみに、匿名クラスを使ってinterfaceのインスタンスを直接作るようなこともできます。(実際にはサブクラスが作られている)

new Hoge1() {
  public String method1() {
    return "hoghogehoge";
  }
}
C#
interface IHoge1 {
  string Method1();
  int Prop1 { get; }
}

C#の場合は基本的には抽象メソッドとプロパティ、インデクサを書くことができます。(eventも書けるといえば書ける…)
アクセス修飾子とかabstractとかは基本的には省略され、C# 7までは書いてあるとコンパイルエラーになります。
また、インタフェース名は慣例的に「I~」という名前にします。(IEnumerableとか)

C#も8からstaticメンバーを持つこととデフォルト実装ができるようになりました。
11からは静的仮想メンバーも使えるらしいです。(今知った)

interface IHoge2 {
  string Method1();
  public static string Method2() {
    return "hoge";
  }
  public string Method3() {
    return "hogehoge";
  }
}

発想はJavaと同じですが、C#にはもともと拡張メソッドという機能があるため、こちらの方がまだ一般的かもしれません。

static class IHogeExt {
  public static string Method3(this IHoge1 hoge) {
    return "hogehoge";
  }
}

継承と実装は区別されず、どちらも「:」で表現されます。

class Fuga : IHoge1 {
  public string Method1() {
    return "hoghogehoge";
  }
}
interface HogeHoge : IHoge1 {
  public string Method4();
}

匿名クラスのような機能はありません。
この辺りはC#の方が早く関数型的な思想を取り入れていたため、どちらかというと無名オブジェクトや無名関数よりもラムダで対応すればいいじゃん的な発想なのかと個人的には思ってます。
(でもソートとか書くとき面倒だなとはときどき思う)

TypeScript
interface Hoge1 {
  method1(): string;
  field1: string;
}

TypeScriptの場合は抽象メソッドとフィールドを書くことができます。
ちなみにJavaScriptにinterfaceという概念がないので、トランスパイルのタイミングで消滅します。

継承と実装は区別されており、Javaと同様にextends、implementsを使います。

class Fuga implements Hoge1 {
  method1(): string {
    return "hoghogehoge";
  }
  field1: string;
}
interface HogeHoge extends Hoge1 {
  method2(): string;
}

面白い機能として、同じ名前でinterfaceを複数定義するとエラーにならずにマージがされます。

interface Fuga {
  name: string;
}
interface Fuga {
  age: number;
}interface Fuga {
  name: string;
  age: number;
}

interface自体のインスタンスを生成することはできませんが、型注釈として利用することができるのも面白いところです。

interface Fuga {
  name: string;
  age: number;
}
const piyo: Fuga = {
  name: "ぴよ",
  age: 3,
};

長い

全部の機能を細かく書いたわけじゃないですが、地味に違いますね。
interfaceとtypeとUnion型について書こうと思ったんですが、なんか長くなっちゃいそうなので分けようかなと思います。