Pair<K,V> を作る
2 つの値を返したいのに、return は 1 個しか書けない
「最小値と最大値をまとめて返したい」「商品コードと在庫数を 1 セットで渡したい」。こういうとき、Java の return は 1 個の値しか返せません。仕方なく int[] に詰めて返すと、受け取る側は [0] が最小値で [1] が最大値だと覚えておかないといけません。名前が付いていないので、順番を取り違えても誰も気づけないのです。
2 つの値に名前を付けて 1 つの型にしてしまえば、この問題は消えます。たとえば「範囲」を表す小さなクラスは次のように書けます。
Java
public class Range<T> {
private final T min;
private final T max;
public Range(T min, T max) {
this.min = min;
this.max = max;
}
public T lower() {
return min;
}
public T upper() {
return max;
}
}Range<Integer> なら整数の範囲、Range<LocalDate> なら日付の範囲になります。フィールドを final にしておくと、作ったあとで書き換えられない箱になります。HashMap のキーに使ったあとに中身が変わってキーを見失う、という厄介な事故を防げます。
下限と上限で型が違うと、これでは足りない
Range<T> が使えるのは、2 つのフィールドが同じ型のときだけです。「文字列のラベルと整数の個数」のように型が違う 2 つを持たせたいときは、型パラメータをカンマで並べて 2 つ宣言します。
Java
public class Labeled<L, V> {
private final L label;
private final V value;
// コンストラクタと取り出しメソッドは Range と同じ形で書く
}宣言が <L, V> の 2 つになっただけで、考え方は 1 つのときと変わりません。Labeled<String, Integer> と書けば、label は String、value は Integer に決まります。標準ライブラリの HashMap<K, V> や Map.Entry<K, V> も、まったく同じ仕組みで 2 つの型を受け取っています。
型パラメータの名前は 1 文字が慣習です。T は Type、E は Element、K と V は Key と Value、R は Result を表します。キーと値を持たせるなら <K, V> を選ぶと、読む人にすぐ伝わります。
決まりきったコードを毎回手で書くのが面倒
Range<T> を見返すと、フィールドとコンストラクタと取り出しメソッドが、ほとんど決まりきった形で並んでいます。値を入れるだけのクラスにこれを毎回書くのは退屈です。Java 16 以降なら record が使えます。
Java
public record Range<T>(T min, T max) {}この 1 行で、final フィールド、コンストラクタ、min() max() の取り出しメソッド、そして equals hashCode toString が自動で作られます。型パラメータを 2 つにしたいときは、record でもクラス名の右にカンマで並べます。
取り出しメソッドの名前が getMin() ではなく min() になる点だけ気をつけてください。
自動で作られる equals が効いてくるのは、HashSet に入れて重複を消したいときです。普通のクラスだと、中身がまったく同じでもインスタンスが別なら別物として扱われます。record なら値が同じかどうかで判定してくれます。
よくある間違い
型パラメータを 2 つ宣言したときに、使う側で順番を逆に渡してしまう事故がよく起きます。Map<K, V> と同じくキーを先、値を後、の並びで揃えておくと迷いません。
もう 1 つ、setLabel のような setter を生やして中身を書き換えられるようにするのはやめましょう。値を持つだけのクラスは、作り直すほうが安全です。
要件
Solutionの中 (内部クラスでも record でも可) に、複数型パラメータ<K, V>を持つPair相当のクラスを定義することformatPair(String name, int age)の中でPair<String, Integer>をnewで生成し、keyとvalueを取り出してname:age形式の文字列を組み立てること- 戻り値の区切り文字は半角コロン (
:) ちょうど 1 つで、前後にスペースを入れないこと
入出力例
formatPair("Alice", 30) → "Alice:30"
formatPair("Bob", 25) → "Bob:25"
formatPair("", 0) → ":0"
formatPair("タロウ", 120) → "タロウ:120"