2013-07-11 31 views
0

我剛開始使用抽象類型。我遇到了一個我無法完全理解的錯誤。下面是我的一些代碼,一些背景在Scala中使用抽象類型的錯誤

abstract class DbfReader(fileName: String) 
{ 
    type DBFDataType <:Any 
    type Key <:Any 
    type Value <:Any 

    abstract class FieldMapping 
    { 
     type FieldType 
     def acronym: Key 
     def longName: Key 
     def fieldNum: Int 
     def getField: FieldType 
     def getFieldLength: Int 
    } 

    def fieldMappings: Map[ Key, FieldMapping ] 
    def getFieldCount: Int 
    def hasRecord(): Boolean 
    def getRecord(): DBFDataType 
    def getFieldVal(fieldName: Key)(rowData: DBFDataType): Value 
    protected def createFieldMapping(fieldAcro: Key, 
             fieldLongName: Key, 
             fieldPosition: Int): FieldMapping 
.... 
} 

抽象類DbfReader意味着是一個抽象的包裝不同的DBF(數據庫文件)讀庫我想要的。抽象類有一個FieldMapping的內部類(表元數據有一個抽象類型FieldType,它意味着數據庫字段的底層庫表示的佔位符,內部類中的例程getField返回這種類型的引用。

以下是該抽象類的沈志南實現:更背景

class MyDBFReader(fileName: String, fmap: List[(String, String, Int)] ) extends DbfReader(fileName) 
{ 
    type DBFDataType = Array[Object] 
    type Key = String 
    type Value = String 

    val dbReader = new jdbf.DBFReader(new java.io.FileInputStream(theFile)) 

    val fieldMappings = addFieldMappings(fmap)(Map()) 
    case class InnerFieldMapping(acronym: Key, longName: Key, fieldNum: Int) extends FieldMapping 
    { 
     type FieldType = jdbf.JDBField 
     override def getField: jdbf.JDBField = dbReader.getField(fieldNum) 
     def getFieldLength = getField.getLength 
    } 

    def getFieldCount = dbReader.getFieldCount 
    def hasRecord = dbReader.hasNextRecord 
    def getRecord = dbReader.nextRecord 
    def createFieldMapping(fieldAcro: String, fieldLongName: String, fieldPosition: Int) = InnerFieldMapping(fieldAcro, fieldLongName, fieldPosition) 

    def getFieldVal(fieldName: Key)(rowData: DBFDataType) = { 
     if(fieldMappings.keySet.contains(fieldName)) stringer(rowData(fieldMappings(fieldName).fieldNum - 1)) 
     else 
      throw new NoSuchElementException("Key " + fieldName + " not Found") 
    } 

    private def stringer(r: Object) = r.asInstanceOf[String].trim 
} 

我遇到麻煩的是,當我嘗試調用從InnerFieldMapping getfield命令它擴展了抽象的字段映射。我正在嘗試這樣的單元測試:

問題出在哪裏發生

class MyDBFSuite extends FunSuite { 
    val fileName = "/Users/Me/api11bdb.dbf" 
    val dbf = new MyDBFReader(fileName, DbfReader.SchoolFieldMapping) 

    test("Dbf should have 150 fields") 
    { 
     assert(dbf.getFieldCount === 150) 
    } 

    test("Should read a record") 
    { 
     assert(dbf.hasRecord === true) 
     assert(dbf.getRecord.size === 150) 
    } 

    test("Should Get a Field") 
    { 
     println(dbf.fieldMappings.head._2.getField.getType) 
     //assert(dbf.fieldMappings.head._2.getField.getType === "S") 
    } 

在最後的測試(或者啓用斷言或println的),每當我試圖訪問的getType這是DBFField例行這是我所期望從內部類InnerFieldMapping例程getField。在抽象的玻璃常規指定FieldType返回類型,這是我在具體的類作爲jdbf.JDBFField

但是編譯器說:的問題

src/test/scala/ChinaDBFTestSuite.scala:23: value getType is not a member of MyDBFSuite.this.dbf.FieldMapping#FieldType 
[error]   println(dbf.fieldMappings.head._2.getField.getType) 
[error]             ^

在其他測試中,我打電話外部類例程getRecord在其抽象類中返回抽象類型。編譯器在那裏沒有問題。看看錯誤消息,它似乎期望FieldType符合內部抽象類定義。我的意思是我會執行它來查找MyDBFSuite.this.dbg.InnerFieldMapping.FieldType。我在這裏做了一些固有的錯誤嗎?編輯: 非常感謝您的回答。作爲後續,我還有一個關於壓倒一切的問題?我注意到,在scala書中,返回抽象類型的方法在子類型中被覆蓋,但是我不這樣做,編譯器在實例化子類型時不會抱怨丟失的實現。爲什麼當方法的返回類型是抽象類型(如基類中定義的)時,子類方法中需要使用override標記?

+0

也許你需要調用'getClass'? – 4lex1v

回答

1

我認爲這是問題,最好用一個例子來說明。你的父類中定義的:

DEF fieldMappings:地圖[關鍵,FieldMapping]

而這必須綁定,均勻單一類型它的編譯器中調用。例如,如果您在上面添加了第二個類,那麼第二個「類型」會合法地開始將其添加到該地圖。 (完全合法,都是FieldMapping的子類。)但是,接下來,Scala會知道稍後要做什麼?

dbf.fieldMappings.head._2.getField.getType 
    dbf.fieldMappings.tail._2.getField.otherFunction 

現在考慮一下,編譯器在.getField之後不知道你在那裏加載了哪兩個類。也許頭是你的第一個孩子班?也許他們都是你所定義的「其他功能」的第二類?誰知道?

解決方案:

你既可以將它轉換後的事實:

dbf.fieldMappings.head._2.getField match { 
    case answer:jdbf.JDBField => answer 
    case _ => throw new ClassCastException 
} 

或者,結合一切從自上而下的類型參數開始 - 這可能是最可讀的和安全的方式,因爲它可以在編譯期間捕獲。

abstract class DbfReader[T](fileName: String) 
{ 
    type DBFDataType <:Any 
    type Key <:Any 
    type Value <:Any 

    abstract class FieldMapping 
    { 

     def acronym: Key 
     def longName: Key 
     def fieldNum: Int 
     def getField: T 
     def getFieldLength: Int 
    } 

    def fieldMappings: Map[ Key, FieldMapping ] 
    def getFieldCount: Int 
    def hasRecord(): Boolean 
    def getRecord(): DBFDataType 
    def getFieldVal(fieldName: Key)(rowData: DBFDataType): Value 
    protected def createFieldMapping(fieldAcro: Key, 
             fieldLongName: Key, 
             fieldPosition: Int): FieldMapping 
.... 
} 

總之,你不能有一個抽象類,不知怎的,使通話以後孩子類型,而在運行時鑄造的集合,即使你只有一個定義,它仍然同樣的問題。

+0

非常感謝,對不起,我沒有及時回覆......有道理,我不能持有抽象對象(包含抽象類型),並期望能夠訪問具體類型的內部結構。因此,如果我想堅持上述方法,我需要確保調用者不需要與底層類型進行交互(考慮到他們只能處理抽象版本)。 –

+0

沒問題,很高興我可以提供幫助,我也在學習Scala的類型系統,它有時真的令人沮喪!我推薦一本好書 - 「Scala for the Impatient」..可能是現在最好的一本,在亞馬遜上便宜。 – LaloInDublin