2016-06-20 4 views
1

Я создал надуманный пример ниже, чтобы показать, что я пытаюсь выполнить. Я ищу фабрику, которая выплюнет класс, который параметризуется с определенным типом запроса, который он обработает. Я думал, что понял дженерики, но теперь я сомневаюсь. :)Scala: несоответствие типов с вложенными генериками

object SourceFactory { 
    def apply[T <: Source[_ <: RequestContext]](x: String): Source[_ <: RequestContext] = { 
    x match { 
     case "a" => new FooSource 
    } 
    } 
} 

object RequestContextFactory { 
    def apply[T >: RequestContext](x: String): T = { 
    x match { 
     case "a" => FooRequestContext() 
    } 
    } 
} 

abstract class RequestContext 
case class FooRequestContext() extends RequestContext 

abstract class Source[T <: RequestContext] { 
    def get(ctx: T): Unit 
} 

class FooSource extends Source[FooRequestContext] { 
    def get(ctx: FooRequestContext): Unit = {} 
} 

object Test extends App { 
    val source = SourceFactory("a") 
    val ctx = RequestContextFactory("a") 

    source.get(ctx) 
} 

Результаты в:

Compiler exception error: line 32: type mismatch; found : Evaluator__da15fb805d29b29227bc28ccae6cd07d2c04cb40_1274353927.Test.ctx.type (with underlying type RequestContext) required: _$2 source.get(ctx)

Заранее спасибо за помощь!

ответ

1

Scala не имеет абсолютно никакого способа сделать вывод T либо в SourceFactory.apply, либо в RequestContextFactory.apply, поскольку он не фигурирует в аргументах. Вы звоните SourceFactory[Nothing] и RequestContextFactory[RequestContext] (и второй только из-за вероятной опечатки в def apply[T >: RequestContext]).

Вы можете дать T явно:

object SourceFactory { 
    def apply[T <: RequestContext](x: String): Source[T] = { 
    (x match { 
     case "a" => new FooSource 
    }).asInstanceOf[Source[T]] 
    } 
} 

object RequestContextFactory { 
    def apply[T <: RequestContext](x: String): T = { 
    (x match { 
     case "a" => FooRequestContext() 
    }).asInstanceOf[T] 
    } 
} 

object Test extends App { 
    val source = SourceFactory[FooRequestContext]("a") 
    val ctx = RequestContextFactory[FooRequestContext]("a") 

    source.get(ctx) 
} 

Обратите внимание, как вы в конечном итоге нужно слепки: это должно намекать, что это плохой дизайн! Проблема в том, что ваше возвращение типа зависит от значения аргумента. Scala поддерживает форму названных зависимых от пути типов, но здесь это не применимо. Можно также попытаться удалить параметры типа и использовать только экзистенциалы:

object SourceFactory { 
    def apply(x: String): Source[_ <: RequestContext] = { 
    x match { 
     case "a" => new FooSource 
    } 
    } 
} 

object RequestContextFactory { 
    def apply(x: String): RequestContext = { // equivalent to _ <: RequestContext 
    x match { 
     case "a" => FooRequestContext() 
    } 
    } 
} 

object Test extends App { 
    val source = SourceFactory("a") 
    val ctx = RequestContextFactory("a") 

    source.get(ctx) 
} 

Не существует ни одного забросов в SourceFactory или ResourceContextFactory, но и не существует никаких отношений между их типами возвратных, так source.get(ctx) не компилируется!

Что действительно может работать, в зависимости от ваших требований, является пара возвращаемых значений в один тип:

case class SourceAndRequestContext[T <: RequestContext](src: Source[T], ctx: T) { 
    def get() = src.get(ctx) 
} 

object SourceAndRequestContextFactory { 
    def apply(x: String): SourceAndRequestContext[_ <: RequestContext] = x match { 
    case "a" => SourceAndRequestContext(new FooSource, FooRequestContext()) 
    } 
} 
+0

Это имеет смысл, спасибо много! – richid