Frage

Ich wollte eine Schnittstelle zum Kopieren eines Objekts in ein Zielobjekt derselben Klasse erstellen.Der einfache Weg ist die Verwendung von Casting:

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);
}
}

Aber ich habe auch eine Möglichkeit gefunden, dies mithilfe von Generika zu erreichen:

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);
    }
}

Die einzige Möglichkeit, die ich gefunden habe, um Warnungen loszuwerden, ist jedoch die Anmerkung.Und es fühlt sich an, als ob etwas nicht stimmt.Also, was ist falsch?Ich kann akzeptieren, dass an der Wurzel des Problems etwas nicht stimmt.Daher ist jede Art von Klarstellung willkommen.

War es hilfreich?

Lösung 6

Ich habe Scala gelernt und ich weiß jetzt, dass das, was ich vor 2 Jahren wollte wurde mit kontra Typ-Parametern und Scala Typ-System erreicht könnte:

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-Typ-System ist zu schwach dafür.

Andere Tipps

Ihre Schnittstellendefinition:

public interface Copyable<T extends Copyable<T>> {
    void copy(T copyFrom);
}

Ihre Implementierung:

public class Example implements Copyable<Example> {
    private Object data;
    void copy(Example copyFrom) {
        data = copyFrom.data;
    }
    //nontrivial stuff
}

Das sollte Ihre Warnungen kümmern.

Angenommen, Sie wollen nicht weiter zu Unterklasse müssen Sie nur:

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

Für eine abstrakte Klasse, Sie tun, um die "Enum" Sache:

public static abstract class AClass<T extends AClass<T>> implements Copyable<T> {

In testCopy besteht eine der Warnungen darin, dass Sie einen „Rohtyp“ von Copyable instanziieren und nicht ein konkretes Copyable<T>.Sobald Sie ein Copyable instanziiert haben, kann es nur auf Ts angewendet werden (einschließlich Untertypen von T).Um mit einem formalen Typ zu instanziieren, müssen die Klassendefinitionen leicht geändert werden:

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

Das nächste Problem besteht darin, dass einem Copyable<B> nur der Typ B zur Kompilierungszeit übergeben werden kann (basierend auf der Definition von Copyable).Und testCopy() oben übergibt ihm einen Copyable-Typ zur Kompilierungszeit.Nachfolgend finden Sie einige Beispiele dafür, was funktionieren wird, mit kurzen Beschreibungen:

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>();
}

ich immer wieder versuchen, einen Weg, um herauszufinden, die Warnungen in Ihrem ersten Ansatz, um loszuwerden, und ich kann mit allem, was nicht kommen, das funktioniert. Trotzdem denke ich, dass der erste Ansatz das kleinere Übel ist. Ein unsicherer Besetzung ist besser als um Ihre Klassen eine solche komplizierte api zu geben.

Ein völlig getrennter Ansatz wäre, außer Kraft zu setzen Object.clone () und implementieren klonbar.

Dies ist der bestmögliche-Code des zweiten Ansatzes. Es kompiliert ohne Warnungen.

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) {
        }
    }
}

Und ich dachte auch aus alles, was mit Hilfe der Reflexion in diesem Programm passiert:

       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());
               }

       }

Hier ist die Ausgabe:

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)

Es bedeutet, dass:

  1. Die Kopie (...) Methoden in A- und B-Make-Compiler generieren "Brücken" - 2 verschiedene Methoden für jede, eine mit reifed Argumenttyp aus Vorfahren (verdinglichter T von Kopierbarer wird Object, verdinglichter „T erstreckt A“von A wird A), und das ist, warum es überschreiben ist und nicht überlasten, und das andere mit verdinglichten Argument Typ für die Klasse definieren. Zuerst Verfahren (mit automatisch generierten Körper) downcasts ihr Argument die nennen Sekunde (sie nennen es eine Brücke). Aufgrund dieses Downcasting wir bekommen Classcast in Laufzeit, wenn wir nennen b1.copy (a).

  2. Es sieht aus wie direkte Art Guss sauberere und besseres Werkzeug ist für meine Problem und Generika sind besser in ihrem unmittelbaren Zweck verwendet - zu erzwingen Typüberprüfung kompilieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top