2017-02-27 17 views
1

Scalaに抽象型をアップキャストしないように指示するにはどうすればよいですか?ブルドッグを考慮

trait Animal { 
    type Food 

    def defaultFood(): Food 
} 
class Bulldog extends Animal { 
    type Food = Steak 

    ... implementations ... 
} 

(私のシンタックスハイライトがエラーを与えたものの、これは大したことないです)コンパイラのためだけで正常に動作しBulldog.defaultFood()機能:しかし

val bulldog = new Bulldog() 
val df: bulldog.Food = bulldog.defaultFood() 

ブルドッグが別の内部に封入されている場合は、クラスは、すべての地獄が緩んで破る:

class Kennel(val animal: Animal) { 
} 
def getSupply(kennel: Kennel): kennel.animal.Food = { 
    ... implementation ... 
} 

val kennel = new Kennel(bulldog) 
val df2: bulldog.Food = getSupply(kennel) 

Scalaのコンパイラは、コンパイルエラーがスローされます:

type mismatch; 
found : Option[kennel.animal.V] where val kennel: Kennel 
required: bulldog.Food 

現在、この機能はScalaにはありませんか?それを動作させる方法はありますか?

+0

質問:オブジェクト食品ではなく、ブルドッグの実装で 'type Food = Steak'と書くつもりはありませんか? –

+0

私の悪い、申し訳ありません。 – tribbloid

+0

あなたは 'getSupply()' _does_を定義しておらず、戻り値の型しか持っていません。 – jwvh

答えて

3

Kennelはパブリックフィールドとしてanimalを公開していないため、コードにコンパイルの問題があります。kennel.animalは解決できません。これはの前にvalを追加することで簡単に解決されます。

Kennelは、Animal以外の、基礎となる動物についての知識がないにもかかわらず、気になるようなことがあるようです。ブルドッグを渡すことは、いくつかの動物を渡すように見えます。

Kennelにいくつかのより多くの種類の情報を追加してみてください:今すぐ

class Kennel[A <: Animal](val animal: A) { } 

def getSupply[A <: Animal](kennel: Kennel[A]): kennel.animal.Food 

val kennel = new Kennel(bulldog) 
val df2: bulldog.Food = getSupply(kennel) 

Kennelanimal(例えばBulldog)とgetSupplyの正確な種類は、その動物のための食品の正確な型を返すことができる知っています。

trait Steak { 
    override def toString() = "steak" 
} 

trait Animal { 
    type Food 
    def defaultFood(): Food 
} 

class Bulldog extends Animal { 
    type Food = Steak 
    def defaultFood() = new Steak {} 
} 

class Kennel[A <: Animal](val animal: A) 

object Test extends App { 

    def getSupply[A <: Animal](kennel: Kennel[A]): kennel.animal.Food = kennel.animal.defaultFood() 

    val bulldog = new Bulldog() 
    val kennel = new Kennel(bulldog) 
    val df2: bulldog.Food = getSupply(kennel) 

    println(df2) // prints "steak" 
} 
+0

私は少しスピンオフの質問があります。なぜこのようなことをやるのか?なぜ、Animal特性が 'defaultFood() 'メソッドを宣言するだけで十分ではないのですか?これは、別の特性である' Food'のインスタンスを返しますか? 'Kennel'と' getSupply() 'がジェネリックであることが重要なのはなぜですか?渡されたパラメータが確実に特性の実装になります(' Animal'がパラメータの型として設定されている場合)。 –

+1

@AleksandarStojadinovicそれはあなたが必要なものによって異なります。私は@tribbloidが「ステーキ」のdf2を食べたいと思っていますが、これは食べ物だけでなく、ブルドッグの好きな食べ物です。同様に、パラメータの型が 'Animal'であると言うと、渡されたパラメータはそのパラメータの実装であることがわかりますが、どちらがどちらか分かりません。あなたは別の動物の区別を失う。 'A <:Animal'を使うことによって、使用された正確な' A'を参照することができます。このようにして、犬舎は「ある種の動物型」を保持するだけでなく、正確な動物型(この例では「ブルドッグ」)を知っています。 – slouc

+0

いいえ、意味があります、tnx。私はそれがあなたから来ていると思っている学校(またはJava/C#の影響の大きさ:-)にも関係していると思います。 –

0

パラメトリック多型(すなわち、型クラス) これをモデル化するためのより良い方法がある:

は、ここにあなたが試すことができる完全に動作するコードです。ここでの主な操作は、あなたが動物を持っており、 にその好きな食べ物のインスタンスを取得する必要があるということです。これをtypeclass にして、特定の動物やその好きな食べ物のインスタンスを提供します。 は、例えば:これはKennelクラスに前方ジェネリック型を伝播するが、理由はScalaの型推論の

@annotation.implicitNotFound(
    "Couldn't confirm that ${Animal}'s favourite food is ${Food}") 
trait DefaultFood[Animal, Food] { def apply(animal: Animal): Food } 
object DefaultFood { 
    /** Helper to easily implement typeclass instances. */ 
    class Impl[Animal, Food](getFood: Animal => Food) 
    extends DefaultFood[Animal, Food] { 
    override def apply(animal: Animal): Food = getFood(animal) 
    } 
} 

class Bulldog 
object Bulldog { 
    type Steak = String // Or whatever. 

    implicit val defaultFood: DefaultFood[Bulldog, Steak] = 
    new DefaultFood.Impl(_ => "Steak") 
} 

class Kennel[Animal, Food](
    animal: Animal)(implicit defaultFood: DefaultFood[Animal, Food]) { 
    def getSupply: Food = defaultFood(animal) 
} 

object Test { 
    val kennel = new Kennel(new Bulldog) // Kennel[Bulldog, Bulldog.Steak] 
} 

、あなたが実際に 種類を綴るする必要はありません。また、特定の 動物とその食べ物についての型クラスインスタンスがない場合、@implicitNotFound注釈は素晴らしい コンパイルエラーメッセージを表示します。 AnimalFood種類が実際に 動物や食べ物である必要はありませんこと

注意。それはここで使用しているセマンティクスです。 最も一般的な方法で見てみると、このタイプクラフトは実際にはABの場合はA => Bタイプの関数の です。

関連する問題