Kotlin 中抽象 class 的实例并在不覆盖的情况下调用抽象函数

Instance of abstract class in Kotlin and calling abstract function without overriding

我正在做这个 google 代码实验室 android-room-with-a-view-kotlin。这是link到codelab。在创建房间数据库的第 8 步,他们使用了此代码

// Annotates class to be a Room Database with a table (entity) of the Word class
@Database(entities = arrayOf(Word::class), version = 1, exportSchema = false)
public abstract class WordRoomDatabase : RoomDatabase() {

   abstract fun wordDao(): WordDao

   companion object {
        // Singleton prevents multiple instances of database opening at the
        // same time. 
        @Volatile
        private var INSTANCE: WordRoomDatabase? = null

        fun getDatabase(context: Context): WordRoomDatabase {
            // if the INSTANCE is not null, then return it,
            // if it is, then create the database
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                        context.applicationContext,
                        WordRoomDatabase::class.java, 
                        "word_database"
                    ).build()
                INSTANCE = instance
                // return instance
                instance
            }
        }
   }
}

我想问一下为什么我创建这个抽象的实例 class (in class A) 然后调用的这两行 (val exam & val dao) 没有错误它的抽象 function(getNoteDao) 而不覆盖它。

class A{

val exam : WordRoomDatabase = WordRoomDatabase.getDatabase(application) 
val dao = exam.getWordDao()
}

因为我们知道我们需要覆盖 abstract function 并且不能直接调用它但是那里发生了什么。为什么没有错误

exam 行没有错误,因为 getDataBase 是一个“伴随”对象,或者如果您来自 Java 世界,则意味着它是一个“静态”对象摘要中的函数 class。这意味着

  • 伴生对象中的函数(或静态函数)属于 CLASS,而不是 class
  • 的实例
  • 您不能在 class 的实例上调用 static/companion 对象函数,所以请注意当您调用“WordRoomDatabase.getDatabase...”时末尾没有括号的“WordRoomDatabase”。您不需要创建它的实例来调用 getDatabase 函数

第 2 行没有错误,这有点难以发现。 在 getDatabase() 中你是 Room.dataBaseBuilder(...) 并传入摘要 class。在该构建器内部,android 实际上创建了抽象 WordRoomDatabase 的实例。 class 并覆盖您的抽象 wordDao 函数

如果您使用的是 AndroidStudio,请构建您的代码。完成后,WordRoomDatabase 旁边的列上会出现一个向下的绿色小箭头。如果单击它,您将能够看到 Room 生成的 class 覆盖了您的抽象函数

您不会收到任何错误,因为这些抽象 classes 的实现是由 kapt 在编译时自动生成的。如果您仔细查看 build.gradle 文件,您会发现它包含一个格式为

的依赖项
kapt 'androidx.room:room-compiler:X.X.X'

这里kapt代表kotlin注解处理工具,它处理你所有class带有某些注解如@Database的Room或 @Dao 并生成它们的实现。例如,我定义了以下@Dao interface

@Dao
interface WordDao {
    @Insert
    fun insert(word: Word)
}

并且 kapt 在执行此 class

后生成
public final class WordDao_Impl implements WordDao {
  private final RoomDatabase __db;

  private final EntityInsertionAdapter<Word> __insertionAdapterOfWord;

  public WordDao_Impl(RoomDatabase __db) {
    this.__db = __db;
    this.__insertionAdapterOfWord = new EntityInsertionAdapter<Word>(__db) {
      @Override
      public String createQuery() {
        return "INSERT OR ABORT INTO `Word` (`someId`) VALUES (?)";
      }

      @Override
      public void bind(SupportSQLiteStatement stmt, Word value) {
        stmt.bindLong(1, value.getSomeId());
      }
    };
  }

  @Override
  public void insert(final Word word) {
    __db.assertNotSuspendingTransaction();
    __db.beginTransaction();
    try {
      __insertionAdapterOfWord.insert(word);
      __db.setTransactionSuccessful();
    } finally {
      __db.endTransaction();
    }
  }
}

WordRoomDatabase也是如此,它的实现也是自动生成的。如果你想查看这些 classes 你总是可以在 \app\build\generated\source\kapt\debug\yourpackage 找到它们,它们标有 _Impl 后缀。