2011-07-24 13 views
5

następujące deklaracje Scala są OK:Scala generics: Int nie odpowiada porównywalnemu?

trait Base[B <: Base[B,M,ID], M <: Meta[B,M,ID], ID <: Comparable[ID]] { 
    // ... 
} 

trait Meta[B <: Base[B,M,ID], M <: Meta[B,M,ID], ID <: Comparable[ID]] extends Ordered[Meta[_,_,_]] { 
    // ... 
} 

trait BaseWithID[B <: BaseWithID[B,M,ID], M <: Meta[B,M,ID], ID <: Comparable[ID]] extends Base[B,M,ID] with Ordered[B] { 
    // ... 
} 


trait BaseWithIntID[B <: BaseWithIntID[B,M,ID], M <: MetaWithIntID[B,M,ID], ID <: Comparable[ID]] extends BaseWithID[B,M,ID] { 
    // ... 
} 

trait MetaWithIntID[B <: BaseWithIntID[B,M,ID], M <: MetaWithIntID[B,M,ID], ID <: Comparable[ID]] extends Meta[B,M,ID] { 
    // ... 
} 

Ale po dwa nie są:

trait BaseWithIntID[B <: BaseWithIntID[B,M], M <: MetaWithIntID[B,M]] extends BaseWithID[B,M,Int] { 
    // ... 
} 

trait MetaWithIntID[B <: BaseWithIntID[B,M], M <: MetaWithIntID[B,M]] extends Meta[B,M,Int] { 
    // ... 
} 

Różnica polega na tym, że usuwa parametr typu ID w BaseWithIntID i MetaWithIntID i określony Int wyraźnie w ich odpowiednie cechy podstawowe. Ale to się nie kompiluje, więc czy to oznacza, że ​​Int nie jest porównywalne w Scali? Jeśli tak, to co robię źle? Próbowałem Zamówione zamiast Porównywalne i nie robiło to różnicy.

Używam Eclipse, i jak zwykle, komunikaty o błędach są bezużyteczne:

type arguments [B,M,Int] do not conform to trait BaseWithID's type parameter bounds [B <: BaseWithID[B,M,ID],M <: Meta[B,M,ID],ID <: java.lang.Comparable[ID]] 

To po prostu mówi, że coś jest nie tak, ale nie który parametr typ jest źle i dlaczego. Patrząc na this question, pomyślałem, że mogę spróbować "ID <% Porównywalne [ID]", ale nie jest to zgodne z deklaracją cechy.

Właściwie, to nie działa albo (z tego samego komunikatu o błędzie):

trait TestBase extends BaseWithID[TestBase,TestMeta,Int] 

trait TestMeta extends Meta[TestBase,TestMeta,Int] 
+0

Problem 'ID <% porównawczych [id]' jest automatycznie określa parametr niejawny. Ponieważ cechy nie mogą mieć parametrów, po prostu nie będą działać. Jako "klasa" możesz jednak to zrobić. –

Odpowiedz

8

Int nie jest rzeczywiście porównywalny w scala, na pewno dlatego, że jest w rzeczywistości zaimplementowany jako java int, a nie java.lang.Integer. Nie jestem pewien, że byłoby to niemożliwe, C# struct (typy wartości) może implementować interfejsy, ale nie jest to tutaj.

To, co zwykle robisz, oznacza, że ​​istnieje Ordering dostępny w domyślnym zakresie dla Twojego identyfikatora, z ID : Ordering.

W prostym przykładzie:

import Ordering.Implicits._ 
def max[A : Ordering](x: A, y: A) : A = if (x > y) then x else y 

Stanowi to przepuszczanie Kolejność (który jest tym samym, co java.util.Comparator) do funkcji. Rzeczywiście, deklaracja

def max[A : Ordering](x: A, y: A) 

przekłada się

def max[A](x: A, y: A)(implicit ev: Ordering[A]) 

gdzie ev jest świeża nazwa. Jeśli A: Kolejność pojawia się w klasie, a nie w definicji metody, tak jak w kodzie, to przekłada się na niejawny parametr konstruktora, który będzie przechowywany w polu, jeśli zajdzie taka potrzeba, i będzie dostępny w ukrytym zakresie w klasie.To jest bardziej elastyczny niż zmuszanie się Comparable (Ordered w Scala), ponieważ może on być stosowany w klasie, która nie jest twoje i nie wdrożył porównywalne. Można wybierać pomiędzy różnymi Odering w tej samej klasie też, jeśli tylko przez odwrócenie domyślną: istnieje def reverse : Ordering metoda na Ordering która właśnie to robi.

Na złe strony, nie jest prawdopodobne, VM będzie mógł inline wywołanie metody porównawczej, ale nie było to prawdopodobnie albo z metody interfejsu w generycznym.

Rodzaje które wdrażają Comparable<T> w java automatycznie uzyskać Ordering w zakresie niejawny mocy niejawny sposób (ordered) w obiekcie Ordering. Comparator<T> język Java może być również przekształcony do Ordering (Ordering.comparatorToOrdering).

importowanie Ordering.Implicits._ pozwala na ładną składnię x > y, gdy Ordering[A] jest w domyślnym zakresie.

0

odpowiedź do „Czy to znaczy, że nie jest porównywalna Int w Scala?” jest oczywiście TAK, ponieważ jeśli zamienię Int na java.lang.Integer, to kompiluje się bez błędu. Problem polega na tym, że w końcu muszę utworzyć obiekt opakowania za każdym razem, gdy dostęp do ID, który będzie się często zdarzał i dlatego będzie drogi.

Chciałem określić, że ID jest porównywalne/uporządkowane, dzięki czemu mogę samodzielnie zamówić obiekt BaseWithID i zdefiniować w nim metodę porównania przy użyciu porównywalnych identyfikatorów.

W tej chwili wydaje się, że rozwiązaniem nie jest ustalenie, że ID jest uporządkowany, i pozwolić, aby konkretna implementacja klasy się porównała, zamiast implementować ją jednorazowo w cechę. Czy ktoś ma lepsze rozwiązanie?

+0

Aby dodać do mojej odpowiedzi, w części "drogie", wywołanie metody porównania - Ordering.comare (x, y) - prawdopodobnie nie zostanie zainicjowane, a Ty możesz zapłacić także boks. Czy byłby to w Javie z porównywalne. Owinięcie tak, że x> y może być wywołana, z drugiej strony, oznacza nowy ord.Ops (x).> (R), co z punktu widzenia implementacji nowych Ordering.Ops (DSW x), gdzie jest ORD zamawianie. Jest całkiem możliwe, że można uniknąć tworzenia tego obiektu. Jeśli zajdzie taka potrzeba (benchmark), możesz zadzwonić do ordering.compare bezpośrednio, co nie byłoby dużo gorsze niż wywołanie porównania w java. –

+0

P.S. "możliwe, że można uniknąć tworzenia tego obiektu" oznacza "możliwe, że maszyna wirtualna faktycznie nie utworzy obiektu". Nie jest to tylko możliwe, ale pewne, że możesz uniknąć tworzenia obiektu, nie używając składni x> y. –