0

抽象型を使い始めたばかりです。完全に理解できないエラーが発生しています。背景のコードの一部を次に示します。

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
}

私が遭遇している問題は、抽象的な Field マッピングを拡張する 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" )
    }

DBFField最後のテスト (アサートを有効にするか、または println で) では、内部クラスInnerFieldMappingルーチン getFieldから期待されるルーチンである getType にアクセスしようとするたびに。抽象グラスでは、ルーチンは の戻り値の型を指定します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]                                                     ^

もう 1 つのテストでは、抽象クラスで抽象型を返す外部クラス ルーチン getRecord を呼び出します。コンパイラには問題はありませんでした。エラー メッセージを見ると、FieldType が内部の抽象クラス定義に準拠していると思われます。つまり、MyDBFSuite.this.dbg.InnerFieldMapping.FieldType を探すようにします。ここで本質的に間違ったことをしていますか?

編集:答えてくれてありがとう。フォローアップとして、オーバーライドについても質問がありますか? scala book では、抽象型を返すメソッドがサブタイプでオーバーライドされていることに気付きましたが、ここではこれを行わず、サブタイプをインスタンス化するときにコンパイラが実装の欠落について文句を言いません。メソッドの戻り値の型が (基本クラスで定義されているように) 抽象型である場合、サブクラス メソッドでオーバーライド タグが必要なのはなぜですか?

4

1 に答える 1

1

これが問題だと思います。それは例で最もよく説明されています。親クラスはこれを定義します:

def fieldMappings: Map[キー、FieldMapping]

そして、これは、コンパイラ中に呼び出す単一の型に均一にバインドする必要があります。たとえば、上記の 2 番目のクラス、2 番目の「タイプ」を追加し、合法的にそのマップに追加し始めるとどうなりますか。(どちらも FieldMapping の子クラスであり、完全に合法です。) しかし、次に、Scala は後で何をすべきかをどのように知るのでしょうか?

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

ここで、コンパイラは .getField の後で、そこにロードした 2 つのクラスのどちらを認識していないかを考えてみましょう。多分頭はあなたの最初の子クラスですか?たぶん、それらは両方とも「otherFunction」を定義する作成した2番目のクラスですか? 知るか?

ソリューション:

事後にキャストすることもできます:

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
....
}

要約すると、実行時にキャストせずに後で何らかの方法で子型を呼び出す抽象型のコレクションを作成することはできません。1 つしか定義していない場合でも、それは同じ問題です。

于 2013-07-12T00:06:32.890 に答える