ジェネリック型とは、型でパラメーター化されたジェネリックなクラスまたはインターフェースのことです。 これにより、コンパイラーはそのクラスやインターフェースに対して型の安全性を強制できます。
次の、ジェネリックでないContainerインターフェースを見てみましょう。
class Container {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}
Object型を受け取って返すので、プリミティブ型以外のあらゆる型で動作します。
しかし、これには代償が伴います。Integerを期待してgetを呼び出すコードもあれば、Stringを追加しようとsetを呼び出すコードもあり、その結果、実行時に例外が発生してしまうからです。
ジェネリッククラスとジェネリックインターフェースは、次のような形式をとります。
class ClassName<T1, T2, ..., Tn> { ... }
interface InterfaceName<T1, T2, ..., Tn> { ... }
クラス名やインターフェース名のうしろに続く、山括弧(<>)で囲まれた型パラメーターの部分では、型パラメーター(型変数とも呼ばれます)T1、T2、…、Tnを指定します。
これらをクラスやインターフェースの本体で使うと、ジェネリックな型安全性が得られます。
こちらが、Containerのジェネリック版です。
class Container<E> {
private E object;
public void set(E object) { this.object = object; }
public E get() { return object; }
}
作成する際には、どの型を保持するのかを宣言する必要があり、コンパイラーがその制約を強制します。
// empty <> can infer from context
Container<String> stringContainer = new Container<>();
// compiler knows this is a String, so it is allowed
stringContainer.set("Some string");
// no cast needed, compiler knows it is a String
String result = stringContainer.get();
// this causes a compiler error:
stringContainer.set(42);
リストは、Javaにおける順序付きのコレクションです。
配列とは違い、Listはサイズを大きくして、いくつでも項目を格納できます。
標準的な実装の1つはArrayListで、サイズ変更可能な配列を基盤としています。
もう1つの標準的な実装はLinkedListクラスで、双方向連結リストを基盤としています。
Listは空の場合もあれば、いくつでも(重複も含めて)項目を保持できます。
Listはジェネリックインターフェースで、どんな型のオブジェクトを格納できるかを示すように型付けされています。
たとえば、次のようになります。
List<String> emptyListOfStrings = List.of();
List<Integer> singleInteger = List.of(1);
List<Boolean> threeBooleans = List.of(true, false, true);
List<Object> listWithMultipleTypes = List.of("hello", 1, true);
Listには、要素の追加・削除・取得・存在確認に役立つさまざまなメソッドがあります。
List<Character> vowels = new ArrayList<>(List.of('a', 'e', 'i', 'o', 'i', 'e', 'a'));
int startingSize = vowels.size(); // 7
vowels.add('u'); // vowels is now ['a', 'e', 'i', 'o', 'i', 'e', 'a', 'u']
char a = vowels.get(0); // 'a'
boolean hadI = vowels.remove('i'); // true and vowels is now ['a', 'e', 'o', 'i', 'e', 'a', 'u']
boolean hasI = vowels.contains('i'); // true (still have one more left)
カールは、Exercismのサイトで学ぶ言語のリストを管理したいと考えています。 カールは、新しい言語を追加したり、古い言語を削除したり、特定の言語がリストに入っているかどうかを確認したりできる必要があります。 カールがJavaやKotlinを学びたいと思ったら、とてもわくわくしますね!
カールは、自分の言語リストが空になったらすぐに気づいて、もっと学ぶ言語を探しに行きたいのです!
isEmptyというメソッドを定義して、リストに言語が1つもないときにtrueを返すようにしましょう。
if (languageList.isEmpty()) {
findMoreLanguagesToLearn();
}
カールはKotlinとPythonを学ぶのを楽しみにしています!
addLanguageというメソッドを定義して、カールが学びたい言語を受け取ってリストに追加できるようにしましょう。
languageList.addLanguage("Kotlin");
languageList.addLanguage("Python");
カールは、今はScalaを学びたくないと思いました。
removeLanguageというメソッドを定義して、削除する言語を受け取ってリストから削除できるようにして、カールを手伝いましょう。
languageList.removeLanguage("Scala");
カールは、自分が最初にリストに追加した言語(まだリストに残っているもの)を覚えておきたいのです。
firstLanguageというメソッドを定義して、リストの最初の言語を返すようにしましょう。
String kotlin = languageList.firstLanguage(); // "Kotlin"
カールは、自分が学ぼうとしている言語がいくつあるのか知りたいのです。
countというメソッドを定義して、リストにある言語の数を返すようにして、カールが答えを見つけるのを手伝いましょう。
int two = languageList.count(); // 2
カールは、自分が学びたかったのはPythonだったかRubyだったかを思い出そうとしています。
containsLanguageというメソッドを定義して、カールが確認したい言語を受け取れるようにしましょう。そうすれば答えがわかります!
boolean learningPython = languageList.containsLanguage("Python"); // true
boolean learningRuby = languageList.containsLanguage("Ruby"); // false
カールがJavaやKotlinを学びたいと思っているなら、それはとてもわくわくします!
isExcitingというメソッドを定義して、カールがJavaまたはKotlinを学びたいときにtrueを返すようにしましょう。
javaLanguageList.isExciting() // true
neitherKotlinNorJavaLanguageList.isExciting() // false