2011-07-24 29 views
5

Las siguientes declaraciones Scala están bien:Genéricos de Scala: ¿Int no conforme con Comparable?

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] { 
    // ... 
} 

Pero los dos siguientes no son:

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] { 
    // ... 
} 

La diferencia es que me quita el parámetro de tipo ID en BaseWithIntID y MetaWithIntID, y especificó Int explícitamente en sus respectivos rasgos básicos. Pero esto no se compila, ¿eso significa que Int no es comparable en Scala? Si es así, ¿qué estoy haciendo mal? Intenté Ordered en lugar de Comparable, y no hizo diferencia.

Estoy usando Eclipse, y como de costumbre, los mensajes de error son inútiles:

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]] 

Sólo dice que algo está mal, pero no el parámetro tipo está mal, y por qué. En cuanto a this question, pensé que podría probar "ID <% comparable [ID]" en su lugar, pero eso no es legal en una declaración de rasgo.

En realidad, esto no funciona bien (con el mismo mensaje de error):

trait TestBase extends BaseWithID[TestBase,TestMeta,Int] 

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

El problema con 'ID <% Comparable [ID]' es que define automáticamente un parámetro implícito. Como los rasgos no tienen parámetros, simplemente no funcionará. Como una 'clase', sin embargo, podrías hacerlo. –

Respuesta

8

Int no es comparable en scala, sin duda porque de hecho se implementa como java int, no java.lang.Integer. No estoy seguro de que hubiera sido imposible, C# struct (tipos de valor) pueden implementar interfaces, pero esto no se hace aquí.

Lo que suele hacer es decir que hay un Ordering disponible en alcance implícito para su tipo de ID, con ID : Ordering.

En un ejemplo sencillo:

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

Esto equivale a hacer pasar una Orden (que es el mismo que un java.util.Comparator) a la función. De hecho, la declaración

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

se traduce en

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

donde ev es un nombre fresco. Si A: Ordenar aparece en la clase en lugar de la definición del método, como en el código, se traduce en un parámetro implícito para el constructor, que se mantendrá en un campo si es necesario y estará disponible en el alcance implícito de la clase.Esto es más flexible que forzar que A sea Comparable (Ordered en scala) ya que se puede usar en una clase que no es suya y no ha implementado Comparable. También puede elegir entre diferentes Odering en la misma clase, si solo invierte el valor predeterminado: existe un método def reverse : Ordering en Ordering que hace precisamente eso.

Por el lado malo, no es probable que la máquina virtual pueda alinear la llamada al método de comparación, pero tampoco era probable con un método de interfaz genérico.

Tipos que implementan Comparable<T> en java obtienen automáticamente un Ordering en su alcance implícito en virtud de un método implícito (ordered) en objeto Ordering. Un java Comparator<T> también se puede convertir a Ordering (Ordering.comparatorToOrdering).

importando Ordering.Implicits._ le permite la buena sintaxis x > y, cuando un Ordering[A] está en alcance implícito.

0

La respuesta a "¿Quiere decir que Int no es comparable en Scala?" es obviamente SÍ, porque si reemplazo Int con java.lang.Integer, entonces compila sin error. El problema es que acabo teniendo que crear un objeto envoltorio cada vez que accedo a la ID, lo que sucederá a menudo y, por lo tanto, será costoso.

Quería especificar que ID es Comparable/Ordenado para poder hacer el BaseWithID mismo ordenado, y definir el método de comparación explícitamente en él utilizando los ID comparables.

La solución, por el momento, parece ser no especificar que se ordena ID, y dejar que la clase concreta implemente se compare, en lugar de implementarlo una sola vez en el rasgo. ¿Alguien tiene una mejor solución?

+0

Para agregar a mi respuesta, en el aspecto "caro", la llamada al método de comparación - Ordering.comare (x, y) - probablemente no estará en línea, y puede que también pague boxeo. Así que hubiera sido en Java con Comparable. La envoltura para que x> y pueda llamarse, por otro lado, significa nueva ord.Ops (x).> (Y), que desde un punto de vista de implementación nueva Ordering.Ops (ord, x), donde ord es el orden Es muy posible que la creación de este objeto se pueda evitar. Si es necesario (punto de referencia) puede llamar directamente a ordering.compare, lo que no sería mucho peor que llamar comparar en java. –

+0

P.S. "Es posible que la creación de este objeto se pueda evitar" significa "posible que la máquina virtual no cree realmente el objeto". No es meramente posible, pero seguro que puedes evitar la creación del objeto, al no usar la sintaxis x> y. –