2017-10-05 103 views
0

這就是在我們的應用程序發生的簡化代碼:類轉換異常(「==」覆蓋)

class First { def ==(first:First)= "mystring"} 
case class Second(first:First) 
Second(new First) == Second(new First) 

這給予: java.lang.ClassCastException:java.lang中.String不能轉換爲java.lang.Boolean

我明白我做錯了什麼,但我不明白是什麼。

編輯

我已經改變修改了代碼:

class First { def ==(first:First)= true} 

,現在在REPL錯誤是:

error: type mismatch; 
found : First(in object $iw) 
required: First(in object $iw) 
     Second(new First) == Second(new First) 

更意想不到的對我說:\

UPDATE第二個例子是我的錯。我在REPL中交互式地改變了類,導致了奇怪的結果。從頭開始它的工作原理。仍然是第一個...

回答

1

因爲Second是一個案例類,它並沒有定義或繼承自equals方法,爲Second#equals生成的代碼看起來應該像

override def equals(x: Any): Boolean = x match { 
    case x: Second => first == x.first 
    case _ => false 
} 

第一分公司的==將是你==(First),而不是通常是==(Any),因爲它是更具體的過載。所以你在申報Second時「應該」得到編譯錯誤。

但是從堆棧跟蹤看來,編譯器在某處插入了一個轉換,而不是(x match ...).asInstanceOf[Boolean](first == x.first).asInstanceOf[Boolean]。說實話,我想不出爲什麼會這樣寫。但是這會使Second編譯並且以後的調用失敗。

+1

這個解釋似乎並不成立:編譯器實際上足夠聰明,可以在選擇重載方法調用時考慮返回類型。當我這樣做:'Foo {def bar(x:Any):Boolean = false; def bar(x:String):String = x}',然後'val foo:Boolean = new Foo().bar(「baz」)'正常工作,並返回'false'。 – Dima

+0

@Dima這很奇怪,如果我正確閱讀http://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#overloading-resolution它不應該工作:兩個備選方案是適用的(僅取決於參數類型),並且返回類型僅適用於多態類型。當然,由於規則相當複雜,我可能會錯過一些東西。 –

+0

@Dima另外,因爲編譯器插入了一個轉換(我們知道它的確如此,否則就不會有'ClassCastException'),所以'first == x.first'本身沒有預期的類型,所以編譯器不能使用它來選擇過載。也就是說,根據我的解釋,你需要嘗試'val foo:Boolean = new Foo().bar(「baz」)。asInstanceOf [Boolean]'。 –

2

千萬不要覆蓋==運算符,它由scala編譯器提供,其語義類似於java中的Object.equals方法。這是它總是返回布爾值的原因。所以我認爲當你實現你自己的運算符時,scala會嘗試將結果轉換爲布爾值。

+0

我明白你的答案,但我仍然覺得結果出乎意料。在實際情況中,我們已經通過使用不同的方法解決了這個問題,但這個例外令我感到驚訝。謝謝 – maborg

+0

我測試它內聯repl更好地理解,但我仍然有點bufflk :) – maborg

3

更新後的版本(使用==返回布爾值)適用於我。

您奇怪的錯誤的原因可能是因爲您在REPL中嘗試它,並重新定義了First類,但不是Second

REPL做一些掛羊頭賣狗肉,讓你重新定義類和變量,這通常是不可能在Scala中,這樣你就基本結束了具有First兩個版本,和你的Second定義仍引用舊的一個,而一個發送與new First()構造函數是新的 - 因此不匹配。

只需重新定義Second類,它就會起作用。

順便重新定義==順便說一句,在這些情況下覆蓋equals更爲常見,這會產生相同的效果。 ==的默認實現只是調用equals,因此,其中一個問題是,某個下游可能會在您的某個子類中覆蓋equals,並且由於它沒有達到預期的效果而被難住。

+0

是的,這個問題是由我在REPL交互式地改變它引起的。從頭開始它的工作原理。 think – maborg

+0

重寫equals而不是==工程,但是如果==調用等於爲什麼我的示例失敗? – maborg

+0

你的例子失敗了,因爲你覆蓋它返回一個字符串,而它需要一個布爾值 – Dima