3

オブジェクトを同じクラスのコピー先オブジェクトにコピーするためのインターフェイスを作成したいと考えていました。簡単な方法は、キャストを使用することです。

import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;

@RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable {
    public void copy(Copyable c);
}

public static class A implements Copyable {
    private String aField = "--A--";
    protected void innerCopy(Copyable c) {
        A a = (A)c;
        System.out.println(a.aField);
    }
    public void copy(Copyable c) {
        innerCopy(c);
    }
}

public static class B extends A {
    private String bField = "--B--";
    protected void innerCopy(Copyable c) {
        B b = (B)c;
        super.innerCopy(b);
        System.out.println(b.bField);
    }
}

@Test
public void testCopy() {
    Copyable b1 = new B();
    Copyable b2 = new B();
    b1.copy(b2);
}
}

しかし、ジェネリックを使用してそれを行う方法も見つけました:

import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;

@RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
    public static interface Copyable<T> {
        public void copy(T t);
    }

    public static class A<T extends A<?>> implements Copyable<T> {
        private String a = "--A--";
        public void copy(T t) {
            System.out.println(t.a);
        }
    }

    public static class B<T extends B<?>> extends A<T> {
        private String b = "--B--";
        public void copy(T t) {
            super.copy(t);
            System.out.println(t.b);
        }
    }

    @Test
    @SuppressWarnings("unchecked")
    public void testCopy() {
        Copyable b1 = new B();
        Copyable b2 = new B();
        b1.copy(b2);
    }
}

警告を取り除く唯一の方法は注釈です。そして、何かが間違っているように感じます。では、何が問題なのですか?問題の根本に何か問題があることを受け入れることができます。したがって、あらゆる種類の説明を歓迎します。

4

6 に答える 6

3

これ以上サブクラス化したくない場合は、次のものが必要です。

public static /*final*/ class AClass implements Copyable<AClass> {

抽象クラスの場合、「列挙」を行います。

public static abstract class AClass<T extends AClass<T>> implements Copyable<T> {
于 2009-03-10T20:05:06.373 に答える
2

testCopyでは、警告の1つは、具体的なCopyable <T>ではなく、「rawタイプ」のCopyableをインスタンス化しているためです。Copyableをインスタンス化すると、T(Tのサブタイプを含む)にのみ適用できます。正式な型でインスタンス化するには、クラス定義を少し変更する必要があります。

public static class A<T extends A> implements Copyable<T>
public static class B<T extends B> extends A<T>

次の問題は、Copyable <B>にはコンパイル時タイプのB(Copyableの定義に基づく)のみを渡すことができるということです。そして、上記のtestCopy()は、コンパイル時のタイプのCopyableを渡します。以下は、機能するもののいくつかの例と簡単な説明です。

public void testExamples()
{
    // implementation of A that applies to A and subtypes
    Copyable<A> aCopier = new A<A>();

    // implementation of B that applies to B and subtypes
    Copyable<B> bCopier = new B<B>();

    // implementation of A that applies to B and subtypes
    Copyable<B> bCopier2 = new A<B>();
}
于 2009-03-11T03:59:04.927 に答える
0

私は Scala を学びましたが、2 年前に望んでいたことが、反変型パラメーターと Scala の型システムを使用して達成できた可能性があることを知っています。

trait CopyableTo[-T] {
  def copyTo(t: T)
}

class A(private var a: Int) extends CopyableTo[A] {
  override def copyTo(t: A) {
    println("A:copy")
    t.a = this.a
  }
}

class B(private var a: Int, private var b: Int) extends A(a) with CopyableTo[B] {
  def copyTo(t: B) {
    println("B:copy")
    super.copyTo(t)
    t.b = this.b
  }
}

@Test
def zzz {
  val b1 = new B(1, 2)
  val a1 = new A(3)
  val b2 = new B(4, 5)
  b1.copyTo(a1)
  a1.copyTo(b1)
  b1.copyTo(b2)
}

Java 型システムはこれには弱すぎます。

于 2011-02-22T06:57:56.217 に答える
0

これは、2 番目のアプローチの可能な限り最良のコードです。警告なしでコンパイルされます。

import static org.junit.Assert.fail;

import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;

@RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
    public static interface Copyable<T> {
        public void copy(T t);
    }

    public static class A<T extends A<T>> implements Copyable<T> {
        private String a = "--A--";
        public void copy(T t) {
            System.out.println(t.a);
        }
        @SuppressWarnings("unchecked")
        public static Copyable<Object> getInstance() {
            return new A();
        }

    }

    public static class B<T extends B<T>> extends A<T> {
        private String b = "--B--";
        public void copy(T t) {
            super.copy(t);
            System.out.println(t.b);
        }
        @SuppressWarnings("unchecked")
        public static Copyable<Object> getInstance() {
            return new B();
        }
    }


    @Test
    public void testCopy() {
        Copyable<Object> b1 = B.getInstance();
        Copyable<Object> b2 = B.getInstance();
        Copyable<Object> a = A.getInstance();
        b1.copy(b2); // this works as intended
        try {
            b1.copy(a); // this throws ClassCastException
            fail();
        } catch (ClassCastException cce) {
        }
    }
}

また、リフレクションの助けを借りて、このプログラムで発生するすべてのことを理解しました。

       for (Method method : A.class.getMethods()) {
               if (method.getName().equals("copy")) {
                       System.out.println(method.toString());
               }

       }
       for (Method method : B.class.getMethods()) {
               if (method.getName().equals("copy")) {
                       System.out.println(method.toString());
               }

       }

出力は次のとおりです。

public void com.sbp.core.TestGenerics$A.copy(com.sbp.core.TestGenerics$A)
public void com.sbp.core.TestGenerics$A.copy(java.lang.Object)

public void com.sbp.core.TestGenerics$B.copy(com.sbp.core.TestGenerics$B)
public void com.sbp.core.TestGenerics$B.copy(com.sbp.core.TestGenerics$A)
public void com.sbp.core.TestGenerics$A.copy(java.lang.Object)

だということだ:

  1. A と B の copy(...) メソッドは、コンパイラで「ブリッジ」を生成します。それぞれに 2 つの異なるメソッドがあり、1 つは祖先から具体化された引数型を持ちます (Copyable から具体化された T は Object になり、A から具体化された「T extends A」は A になります) )そしてそれがオーバーロードではなくオーバーライドであり、クラスを定義するための具体化された引数型を持つもう1つの理由です。最初のメソッド (自動生成された本体を持つ)は、その引数をダウンキャストして 2 番目のメソッドを呼び出します (彼らはそれをブリッジと呼びます)。このダウンキャストにより、b1.copy(a) を呼び出すと、実行時に ClassCastException が発生します。

  2. 直接の型キャストは、私の問題に対するよりクリーンで優れたツールであり、ジェネリックは、コンパイル時の型チェックを強制するという直接的な目的でより適切に使用されているようです。

于 2009-03-11T08:53:34.437 に答える