2016-08-23 52 views
2

考慮:定義`copy`爲`trait`

sealed trait X { val x: String } 
case class A(x: String) extends X 
case class B(x: String) extends X 

我在X特質定義的copy方法:

def copy(x: X, newValue: String): X = x match { 
    case A(_) => A(newValue) 
    case B(_) => B(newValue) 
} 

但是,我認爲我可以做的更好,即精確。

因爲在技術上,僅僅因爲輸入了A,所以可以輸出B,因爲BX的子類。

所以,我想:

def copyBetter[T <: X](x: T, newValue: String): T = x match { 
case A(_) => A(newValue) 
case B(_) => B(newValue) 
} 

但是,我得到了編譯時錯誤:

<console>:17: error: type mismatch; 
found : A 
required: T 
      case A(_) => A(newValue) 
         ^
<console>:18: error: type mismatch; 
found : B 
required: T 
      case B(_) => B(newValue) 
         ^

我如何能實現copyBetter與給定的簽名?

+0

我想我有一個類似的問題,我解決了它返回'X',這裏的問題是你的返回類型是動態的,你不能找出哪個類型'T'會在調用之前,這有點不同從普通方法調用到泛型方法,在這種情況下,您可以事先指定類型,因爲您知道將要返回的內容,在這裏您不能。 –

+0

我會看看沒有形狀,只是爲了檢查你的問題是否可以與之相對應。 – Reactormonk

回答

1

只有這樣,才能實現一個方法與此簽名(忽略null)是def copyBetter[T <: X](x: T, newValue: String): T = x!爲什麼?因爲單身人士類型存在。鑑於這種簽名,以下必須編譯,由於x.typeX亞型:

val x = A(1) 
val y: x.type = copyBetter[x.type](x, 2) 

y的類型說y必須是相同的對象作爲x(或null)。因爲在你的實現中不是,這個實現不適合簽名。

+0

'因爲單身人士類型存在'你能說更多或者指向我的鏈接嗎?什麼是單身類型? –

+0

這個例子中'x.type'是一個單例類型。這是具有兩個值的類型:'x'和'null'。 –

1

我建議如下:

def copyBetter[T <: X](x: T, newValue: String): X = x match { 
    case A(_) => A(newValue) 
    case B(_) => B(newValue) 
} 
+0

現在,在這個工作示例中,'copyBetter'仍然可以接受'A',但返回'B'。儘管定義不合理,但我認爲,它會被編譯。有沒有辦法得到更高的一個級別,即需要'X'的輸入子子和輸出相同的類型? –

+0

編譯器無法知道'x'將在運行時匹配哪種情況,所以它選擇最具體的超類型 - 即'X'。我認爲沒有辦法繞過它。 –

+1

它實際上編譯? '_ <:X'只能作爲_argument_類型(例如'List [_ <:X]'),而不能作爲類型本身。我收到「未綁定通配符類型」:http://scastie.org/21879。 –

1

您還沒有返回T而是XAB延伸X而不是T

嘗試與此簽名:

def copyBetter[T <: X](x: T, newValue: String): X = // ... 
+0

感謝這個有用的答案。自從他提前1分鐘回答之後,我接受了Zoltan的。 –

+1

與佐爾坦的答案相同:使用它而不是原始的「副本」絕對沒有好處。 –

+0

如果參數的類型是'X',那麼總是可以傳遞'X'的子元素,這沒有什麼好處?換句話說,約束不會簡單地寫入'x:X'? –

0

簡單 - 你怎麼能指望的返回類型爲T,當它甚至沒有參與方法體。 T是無關緊要的,返回類型是AB的共同祖先。

而且,你不應該將其定義爲:

def copy(newValue: String): X = this match { 
    case _: A => A(newValue) 
    case _: B => B(newValue) 
}