public class A {
@NonNull
public final String name;
public A(@NonNull String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
A a = (A) o;
return name.equals(a.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
public class B extends A {
public final int size;
public B(String name, int size) {
super(name);
this.size = size;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
B b = (B) o;
return size == b.size;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + size;
return result;
}
}
test はこんな感じです。B が A を継承しているのは、A のリストに B を入れたいからです。
public class ABTest {
@Test
public void test() {
final B b = new B("hoge", 10);
assertEquals("hoge", b.name);
assertEquals(10, b.size);
assertEquals(new B("hoge", 10), new B("hoge", 10));
assertNotEquals(new B("hoge", 10), new B("fuga", 10));
assertNotEquals(new B("hoge", 10), new B("hoge", 11));
List<A> aList = new ArrayList<>();
aList.add(new B("hoge", 10));
assertEquals(new B("hoge", 10), aList.get(0));
}
}
こういう Java コードがあって、これを Kotlin 化するときに A, B それぞれを data class にしようとしたら、data class は親クラスになれない(final)ので困りました。このテストが通るように、いい感じに Kotlin 化したいのです。
1. data class にするのを諦める
自分で equals と hasCode を override すれば data class にしなくてもやりたいことはできます。以下は自動変換しただけのもの。
open class A(val name: String) {
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o == null || javaClass != o.javaClass) return false
val a = o as A?
return name == a!!.name
}
override fun hashCode(): Int {
return name.hashCode()
}
}
class B(name: String, val size: Int) : A(name) {
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o == null || javaClass != o.javaClass) return false
if (!super.equals(o)) return false
val b = o as B?
return size == b!!.size
}
override fun hashCode(): Int {
var result = super.hashCode()
result = 31 * result + size
return result
}
}
もちろん上のテストは通ります。しかし、いけてない。
2. A を interface にする
Kotlin では interface に抽象プロパティを持たせることができるので、A を interface に変えてみます。
interface A {
val name: String
}
こうすると、B を data クラスにできます。
data class B(override val name: String, val size: Int) : A
めっちゃ短い!テストもちゃんと通ります。
sealed classを使う、というのも手です。
返信削除https://kotlinlang.org/docs/reference/sealed-classes.html