Дженерики Scala: Int не соответствует Comparable?

Следующие объявления Scala допустимы:

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

Но следующие два нет:

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

Разница в том, что я удалил параметр типа ID в BaseWithIntID и MetaWithIntID и явно указал Int в соответствующих базовых трейтах. Но это не компилируется, значит ли это, что Int не является Comparable в Scala? Если да, то что я делаю не так? Я попробовал Ordered вместо Comparable, и это не имело значения.

Я использую Eclipse, и, как обычно, сообщения об ошибках бесполезны:

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

Это просто говорит о том, что что-то не так, но не о том, какой параметр типа неверен и почему. Глядя на этот вопрос , я подумал, что мог бы вместо этого попробовать "ID ‹% Comparable[ID]", но это недопустимо в объявлении трейта.

На самом деле это тоже не работает (с тем же сообщением об ошибке):

trait TestBase extends BaseWithID[TestBase,TestMeta,Int]

trait TestMeta extends Meta[TestBase,TestMeta,Int]

person Sebastien Diot    schedule 24.07.2011    source источник
comment
Проблема с ID <% Comparable[ID] заключается в том, что он автоматически определяет неявный параметр. Поскольку трейты не могут иметь параметры, это просто не сработает. Однако, как class, вы могли бы это сделать.   -  person Daniel C. Sobral    schedule 26.07.2011


Ответы (2)


Int действительно несопоставим в scala, потому что на самом деле он реализован как java int, а не java.lang.Integer. Я не уверен, что это было бы невозможно, C# struct (типы значений) могут реализовывать интерфейсы, но здесь этого не делается.

Что вы обычно делаете, так это говорите, что в неявной области действия для вашего типа идентификатора имеется Ordering, с ID : Ordering.

На простом примере:

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

Это равносильно передаче Ordering (что то же самое, что и java.util.Comparator) в функцию. Действительно, декларация

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

переводится как

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

где ev — свежее имя. Если A : Ordering появляется в классе, а не в определении метода, как в вашем коде, он преобразуется в неявный параметр конструктора, который при необходимости будет храниться в поле и будет доступен в неявной области видимости в классе. Это более гибко, чем заставлять A быть Comparable (Ordered в scala), поскольку он может использоваться в классе, который не принадлежит вам и не реализует Comparable. Вы также можете выбирать между различными Odering для одного и того же класса, хотя бы изменив значение по умолчанию: для Ordering есть метод def reverse : Ordering, который делает именно это.

С другой стороны, маловероятно, что виртуальная машина сможет встроить вызов метода сравнения, но это также маловероятно с интерфейсным методом в универсальном объекте.

Типы, реализующие Comparable<T> в java, автоматически получают Ordering в неявной области видимости благодаря неявному методу (ordered) в объекте Ordering. Java Comparator<T> также можно преобразовать в Ordering (Ordering.comparatorToOrdering).

import Ordering.Implicits._ позволяет использовать хороший синтаксис x > y, когда Ordering[A] находится в неявной области.

person Didier Dupont    schedule 24.07.2011

Ответ на вопрос «Означает ли это, что Int не сравним в Scala?» очевидно, ДА, потому что, если я заменю Int на java.lang.Integer, он скомпилируется без ошибок. Проблема в том, что мне в конечном итоге приходится создавать объект-оболочку каждый раз, когда я обращаюсь к идентификатору, что будет происходить часто и, следовательно, будет дорого.

Я хотел указать, что идентификатор Comparable/Ordered, чтобы я мог сделать сам BaseWithID упорядоченным и явно определить в нем метод сравнения, используя сопоставимые идентификаторы.

На данный момент решение, по-видимому, состоит в том, чтобы не указывать, что идентификатор упорядочен, и позволить реализации конкретного класса сравнивать себя, вместо того, чтобы реализовывать его один раз в трейте. У кого-нибудь есть лучшее решение?

person Sebastien Diot    schedule 24.07.2011
comment
Чтобы добавить к моему ответу дорогой аспект, вызов метода сравнения - Ordering.comare(x,y) - вероятно, не будет встроен, и вы также можете заплатить за бокс. Так было бы в java с Comparable. С другой стороны, перенос так, чтобы можно было вызвать x › y, означает новый ord.Ops(x).›(y), который с точки зрения реализации new Ordering.Ops(ord, x), где ord заказ. Вполне возможно, что создания этого объекта удастся избежать. При необходимости (бенчмарк) вы можете напрямую вызвать ordering.compare, что будет не намного хуже, чем вызов compare в java. - person Didier Dupont; 24.07.2011
comment
P.S. возможно, что создания этого объекта можно избежать, означает, что виртуальная машина фактически не будет создавать объект. Не только возможно, но и определенно можно избежать создания объекта, не используя синтаксис x › y. - person Didier Dupont; 24.07.2011