2013-08-26 29 views
7

Chciałbym utworzyć ogólny interfejs dla tych dwóch klas, ale nie jestem pewien, jak określić rodzajowe we właściwy sposób.Utwórz ogólny interfejs ograniczony do własnej klasy

public class ThingA implements Thing { 
    public ThingA createCopy(ThingA original); 
} 

public class ThingB implements Thing { 
    public ThingB createCopy(ThingB original); 
} 

Próbowałem tego.

public interface Thing<V extends Thing<V>> { 
    public V createCopy(V original); 

} 

Ale wciąż jestem w stanie robić takie rzeczy, które nie powinny być dozwolone.

public class ThingB implements Thing<ThingA> { 
    public ThingA createCopy(ThingA original); 
} 

Odpowiedz

6

Nie ma generycznych słów kluczowych (ani dla parametrów metod i deklaracji wartości zwracanych), a zatem nie można wykonać dokładnie tego, co chcemy.

Innymi słowy interfejs pozwoli na zapewnienie wszystkich metod w klasie spójnych typów, ale nie na temat samego typu klasy.

0

Szukasz

public interface Thing<V> { 
    public V createCopy(V original); 
} 

? Jeśli nie, czy możesz wyjaśnić bardziej szczegółowo, co oznacza dla ciebie "stworzenie ogólnego interfejsu dla dwóch klas"?

+1

To nadal pozwala mu robić dokładnie to, czego nie chce. Generics nie zapobiega używaniu tylko jednego rodzaju klasy, ponieważ będzie to całkowite przeciwieństwo celu generycznego. – giorashc

1

To nie jest możliwe. I to nie jest to, do czego służy Generics. Generics jest dla bezpieczeństwa typu , tj. Unikając rzutów. Jeśli ktoś tworzy klasę ThingB, która implementuje w pewnym sensie Thing<ThingA>, to świetnie. Jest całkowicie bezpieczny w użyciu. Dlaczego się przejmujesz? Jak to przeszkadza w tym, co robisz?

0

W przypadku, gdy mogą swobodnie korzystać rozszerzenie zamiast realizacji, to można to zrobić w ten sposób:

public interface Thing { ... } 
public abstract class Copyable { 
    public final Copyable copy() { 
     Copyable clone = createCopy(); 
     if (clone.getClass() != getClass()) 
      throw new RuntimeException("Copy has wrong type!"); 
     return clone; 
    } 
    protected abstract Copyable createCopy(); 
} 

a następnie użyć go lubię:

public class Test extends Copyable implements Thing { 
    public String content = null; 

    @Override 
    public Copyable createCopy() { 
     Test clone = new Test(); 
     clone.content = this.content; 
     return clone; 
    } 
} 

/*code somewhere*/ { 
    Test t1 = new Test(); 
    t1.content = "Hello world!"; 
    Test t2 = (Test)t1.copy(); 
    System.out.println(t2.content); 
} 

Jeden problem z tym jest że Copyable nie jest interfejsem. Można to jednak zastosować bez większego bólu, jak widać w przykładzie, ale sprawdzanie klasy nie jest obsługiwane na poziomie języka. Innymi słowy, metoda abstrakcyjna createCopy nie jest ograniczona do klasy, którą kopiuje, a wszystko to zależy od programisty, który rozszerza klasę Copyable lub klasę, która ją rozszerza.

Pozytywną stroną jest to, że jeśli wywołujemy obiekt .copy(), musi on zwrócić obiekt taki sam jak on. Zamiast wyjątku możesz zwrócić null, jeśli chcesz. Wtedy masz dobre lub nic.

Ale szczerze mówiąc, tak naprawdę nie rozumiem, dlaczego lokalna metoda ma parametr. To może być następnie metoda statyczna ... altrough nie mogę sobie nawet wyobrazić, co by przejść do tego bloku kodu:

static <X extends Thing> X copy(X object) { ... } 

Może można połączyć Practice ze statycznej metody rodzajowe i wynik staje się nieco bardziej przyjazny :

public interface Thing extends Cloneable { 
    public static <X extends Thing> X copy(X thing) { 
     Object clone = thing.clone(); 
     if (clone.getClass() != getClass()) 
      throw new RuntimeException("Copy has wrong type!"); 
     return (X)clone; 
    } 
} 

public class ThingA implements Thing { 
    public Object clone() { ... } 
} 

/*code somewhere*/ { 
    ThingA a1 = new ThingA(); 
    ThingA a2 = Thing.copy(a1); 
} 

Mimo to, metoda klonowania jest regulowany przez wyjątek zamiast ograniczenia języka, ale myślę, że jest to zdecydowanie najlepsze rozwiązanie.

Powiązane problemy