2013-07-11 4 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 во внутреннем классе возвращает ссылку этого типа .

Ниже приводится реализация concerete этого абстрактного класса: ДАЖЕ БОЛЬШЕ ФОН

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, который расширяет абстрактное отображение поля. Я пытаюсь сделать это в модульном тесте следующим образом:

ГДЕ ПРОБЛЕМА ПРОИСХОДИТ

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. Я делаю что-то по своей сути неправильно здесь?

EDIT: Большое спасибо за ответ. В качестве продолжения, у меня также есть вопрос об переопределении? Я замечаю, что в книгах Scala методы, возвращающие абстрактные типы, переопределяются в подтипах, однако я не делаю этого здесь, и компилятор не жалуется на недостающие реализации при создании экземпляров подтипов. Почему в методе подкласса требуется переопределяющий тег, когда тип возвращаемого метода является абстрактным (как определено в базовом классе)?

+0

Возможно, вам нужно позвонить 'getClass'? – 4lex1v

ответ

1

Я думаю, что это проблема, это лучше всего иллюстрируется примером. Ваш родительский класс определяет это:

DEF fieldMappings: Карта [Key, FieldMapping]

И это должны быть прошиты, гомогенно к одному типу, что вызов во время компилятора. Например, что, если вы добавили второй класс выше, второй «тип» и юридически начнете добавлять его к этой карте. (Совершенно юридически, оба являются дочерними классами FieldMapping.) Но как тогда, далее, Скала знает, что делать дальше?

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

Теперь учтите, что компилятор понятия не имеет после .getField, который из 2 классов вы загрузили там. Может, голова вашего первого класса ребенка? Может быть, они оба являются вторым классом, который вы создали, который определяет «otherFunction»? кто знает?

Решения:

Вы можете либо бросить его после факта:

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, и иногда это очень неприятно! fyi Я рекомендую хорошую книгу - «Скала для нетерпения» ... наверное, лучший из них сейчас и дешевый на амазонке. – LaloInDublin

Смежные вопросы