2015-06-28 4 views
2

Скажем, у меня есть класс М, что классы A, B, C унаследуют:Scala - возвращение того же типа, как передается в качестве аргумента

abstract M 
A extends M 
B extends M 
C extends M 

И я хотел бы сделать что-то вроде этого:

val a0:A = ... 
val b0:B = ... 
val c0:C = ... 
val a1:A = transform[A](a0) 
val b1:B = transform[B](b0) 
val c1:C = transform[C](c0) 

Где transform по существу делает то же для каждого подтипа и отличается только тем, как создается объект результата.

У меня есть подозрение, что это невозможно, и мне нужно либо скопировать код, либо создать отдельные методы преобразования, либо прибегнуть к приведению типов. Или есть лучший способ?

EDIT

Обратите внимание, что def transform[T<:M](t:T):T не работает. Если мы попытаемся вернуть A, B или C, мы получим сообщения об ошибках ниже.

Выражение типа А, не соответствует ожидаемому типу Т

Выражение типа B не соответствует ожидаемому типу Т

Выражение типа C не соответствует ожидаемому типу Т

EDIT 2 Возможно, некоторые более подробную информацию о том, что я пытаюсь сделать:

transform(m:M) = { 
    val P = Property P computed from m 
    m match { 
     case a:A => construct and return new A from a with property P 
     case b:B => construct and return new B from b with property P 
     case c:C => construct and return new C from c with property P 
     case _ => error 
    } 
} 

Если я делаю это так, то мне нужно слепки:

val a1:A = transform(a0).asInstanceOf[A] 
val b1:B = transform(b0).asInstanceOf[B] 
val c1:C = transform(c0).asInstanceOf[C] 

, который я хотел бы устранить.

ответ

2

Это зависит от реализации transform. Если для компилятора ясно, что transform сохраняет этот тип, то ответ @ Dimitry работает.

Если вам нужны менее очевидные (для компилятора) отношения между типами, тогда обычный способ - шаблон типа.

trait Transformer[T] { 
    def transform(t: T) : T 
} 

def transform[T: Transformer](t: T) = implicitly[Transformer[T]].transform(t) 

implicit object ATransformer extends Transformer[A] { 
    def transform(a: A): A = ... 
} 
implicit object BTransformer extends Transformer[B] { 
    def transform(b: B): B = ... 
} 

Затем вы можете реализовать конкретные преобразования для A/B/C в конкретных объектах, и компилятор только позволит вам позвонить transform, если у вас есть в неявной области подходящей Transformer.

+0

Хорошее дополнение, но User1291 сказал, что мое решение не работает для него. Любая идея? –

+0

Интересно ... в чем преимущество этого, просто перегружая метод преобразования? – User1291

+1

@ Dimitry yes, если вы создаете свой собственный A/B/C, тогда вам нужен шаблон типа types, как я предложил, и это должно сработать. @ User1291 Преимущество состоит в том, что это можно использовать, если метод, который вызывает 'transform', сам по себе является общим (вы просто включаете ограничение типа typeclass [T: Transformer]'). – lmm

3

Это довольно просто:

class M 
class A extends M 
class B extends M 

def transform[T <: M](obj: T): T = { 
    obj 
} 

val a0:A = new A 
val a1:A = transform(a0) 
val b0:B = new B 
val b1:B = transform(b0) 
+1

Возможно, я ударил головой, но не «T>: M» означает, что T - суперкласс M - любой суперкласс M - который является неправильным направлением в иерархии наследования? – User1291

+0

Сори, сделал опечатку. Отредактировав мой ответ, попробуйте этот код –

+0

Я пробовал это, прежде чем я спросил здесь. К сожалению, это не работает (см. Редактирование вопроса). – User1291