В Scala, как мне сказать абстрактному базовому классу, что параметр типа T поддерживает неявное преобразование из Int (или Float, или...)?
У меня возникают трудности при переходе из мира C++/Templates в scala. Я привык иметь возможность использовать любую операцию с параметром шаблона T, которую я хочу, при условии, что все, что я использую для создания экземпляра T с, поддерживает эти операции (в основном, типизация Duck во время компиляции). Я не могу найти соответствующую идиому в Scala, которая позволит мне определять абстрактный класс с одним параметром типа и который ожидает определенный интерфейс для типа T.
То, что у меня есть, почти работает, но я не могу понять, как сказать абстрактному классу (Texture[T <: Summable [T]]), что T поддерживает преобразование / конструирование из Int. Как я могу добавить неявное преобразование в черту Summable, чтобы Texture знал, что T поддерживает преобразование?
trait Summable[T] {
def += (v : T) : Unit
def -= (v : T) : Unit
}
object Int4 { implicit def int2Int4(i : Int) = new Int4(i, i, i, i) }
class Int4 (var x : Int, var y : Int, var z : Int, var w : Int) extends Summable[Int4] {
def this (v : Int) = this(v, v, v, v)
def += (v : Int4) : Unit = { x += v.x; y += v.y; z += v.z; w += v.w }
def -= (v : Int4) : Unit = { x -= v.x; y -= v.y; z -= v.z; w -= v.w }
}
abstract class Texture[Texel <: Summable[Texel]] {
var counter : Texel
def accumulate(v : Texel) : Unit = { counter += v }
def decrement() : Unit = { counter -= 1 } //< COMPILE ERROR HERE, fails to find implicit
}
class Int4Target extends Texture[Int4] {
var counter : Int4 = new Int4(0, 1, 2, 3)
}
3 ответа
Вы можете определить неявный параметр конструктора, как это
abstract class Texture[Texel <: Summable[Texel]](implicit int2Texel: Int => Texel) {
//...
По сути, это говорит компилятору, что для создания экземпляра Texture
должна быть неявная функция преобразования, доступная из Int
в Texel
, Предполагая, что у вас есть такая функция, определенная где-то в области видимости (что вы делаете), вы больше не должны получать ошибку компиляции.
Edit2: Хорошо, я изначально неправильно прочитал ваш код, вам нужен только один неявный параметр из Int => Texel
, Ваш код компилируется для меня с вышеуказанной модификацией.
Изменить: вам на самом деле нужно 2 функции преобразования, одна из Texel => Int
и еще один из Int => Texel
для того, чтобы правильно переназначить переменную
Принципиальное различие между шаблонами C++ и всем остальным в Scala заключается в том, что шаблоны C++ компилируются для каждого использования, то есть, если вы используете шаблон с int
и с double
затем компилируются два разных класса, и они компилируются только тогда, когда какой-то код фактически использует их.
Scala, с другой стороны, имеет отдельный сборник. Не так хорошо, как Java, учитывая ограничения JVM, но все еще следуя основному принципу. Таким образом, если у чего-то есть параметр типа, он все еще компилируется при объявлении, и существует только один такой класс. Этот скомпилированный код должен поддерживать все возможные параметры, чем он может вызываться, что накладывает некоторые другие ограничения, чем шаблоны.
Что касается признаков и неявных преобразований, признаки не поддерживают параметры, а неявные преобразования (границы представления) являются параметрами. Вместо этого используйте класс.
В Scala невозможно требовать неявного преобразования для параметра типа признака. Для этого есть веская причина. Предположим, мы определили такую черту, как:
trait ATrait[T <% Int] {
def method(v: T) { println(v: Int) }
}
И затем сделал примеры этого в двух местах:
package place1 {
implicit def strToInt(s: String) = 5
val inst = new ATrait[String]
}
package place2 {
implicit def strToInt(s: String) = 6
val inst = new ATrait[String]
}
И затем использовал эти экземпляры как:
val a = if (someTest) place1 else place2
a.method("Hello")
Если это печать 5
или же 6
? То есть какое неявное преобразование следует использовать? Последствия должны быть найдены во время компиляции, но вы не знаете, какое неявное преобразование имело место для создания объекта.
Другими словами, последствия обеспечиваются областью, в которой они используются, а не объектами, на которых они используются; последнее было бы невозможно.
Итак, о вашей проблеме. Вместо использования неявного, вы можете использовать обычный член:
trait Summable[T] {
def -= (v: T): Unit
def -= (v: Int) { this -= (encode(v)) }
def encode(i: Int): T
}
class Int4 (var x: Int, var y: Int, var z: Int, var w: Int) extends Summable[Int4] {
def -= (v : Int4) : Unit = { x -= v.x; y -= v.y; z -= v.z; w -= v.w }
def encode(i: Int) = Int4.int2Int4(i)
}
Теперь decrement
Метод компилируется правильно.
Другой способ сказать это - не думать о имплицитах как о свойствах, принадлежащих типу (т. Е. "Может быть неявно преобразовано из Int" не является свойством Int4). Это значения, которые можно идентифицировать с помощью типов.
Надеюсь это поможет.