Kotlin: ссылка на анонимный объект из себя / изнутри (через это) - PullRequest
0 голосов
/ 07 сентября 2018

TL; DR Эти object : someClass{ } анонимные объекты не могут получить доступ к себе через this (в результате внешний объект ). Как я могу получить к нему доступ?

Более подробное объяснение:

Для моего фрагмента мне нужен PreDrawListener. Я называю это в onCreateView. При выполнении я хотел удалить слушателя впоследствии. Таким образом, способ работы Java мог бы предложить что-то вроде этого

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,    

  val treeObserver = layout.viewTreeObserver

  treeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
     override fun onPreDraw(): Boolean {
        layout.viewTreeObserver.removeOnPreDrawListener(this)
        ...
     }
  }

Проблема в том, что при взгляде на removeOnPreDrawListener(this) объект this не слушатель, а myFragment$onCreateView$1@f019bf0

В качестве альтернативы я мог получить доступ к this@MyFragment, который возвращает ссылку на фрагмент напрямую.

Тем не менее, ни один из этих вариантов не является моим PreDrawListener. Как я могу получить к нему доступ изнутри (если вообще есть)?

1 Ответ

0 голосов
/ 07 сентября 2018

Честно говоря, я не вижу твоей проблемы.

this внутри анонимного относится к самому классу, но у них никогда нет имен. Вы не можете создать анонимный класс с именем. Чтобы продемонстрировать это, я написал пример кода:

class TheClass{
    fun run(){
        val anon = object: Runnable {
            override fun run() {}
        }
        println(anon::class.java.simpleName)
        println(anon::class.java.name)
    }
}

Какие отпечатки:

run$anon$1
com.package.TheClass$run$anon$1

Теперь, это хорошо и все такое, но все равно не похоже на ваше. Но вы видите, что он соответствует классу, методу, переменной и, наконец, знаку доллара, обозначающему, что это анонимный внутренний класс. Это относится ко второму, который является полным. Первый просто печатает короткое имя, которое является методом, именем переменной, и снова знак доллара, который показывает его анонимную функцию.

Если вам интересно, почему появляется знак доллара с цифрой, см. this . T

Давайте расширим это и отбросим переменную. Очевидно, это ужасный код (и далеко не эффективный для памяти, но это демо, так что это не имеет значения):

class TheClass {
    fun run(){
        println(object: Runnable {
            override fun run() { }
        })
    }
}

Это печатает и соответствует вашему шаблону:

com.package.TheClass$run$anon$1

Вы видели шаблон; Теперь вы можете начать «расшифровывать» полученный хеш:

myFragment // inside myFragment
$onCreateView // Inside a function
$1 // There is an anonymous class with a specific identifier
@f019bf0 // This is standard everywhere; just look up Object.toString()

Я только что попытался доказать: this относится к созданной вами анонимной функции. Анонимные функции, ну, в общем, анонимные. У них нет имен. Они используют $number в качестве идентификаторов. Так что, если у вас есть этот код:

treeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
    override fun onPreDraw(): Boolean {
       layout.viewTreeObserver.removeOnPreDrawListener(this)
       ...
    }
 }

this будет относиться к слушателю, даже если печать класса может напечатать материал, который выглядит запутанным. Если что-то сломано, это не потому, что this не ссылается на слушателя (потому что это так)

Кроме того, ваш код компилируется нормально. Там тоже нет несоответствия типов. Если бы он ссылался на другой объект, он бы не работал, если вы передали this методу, который требует OnPreDrawListener.


Вы получите другой результат с тем же кодом на Java. Это потому, что Kotlin компилирует анонимные функции как Class$function$number, а Java компилирует его в Class$number. Если он находится во вложенном классе, он будет отображаться как Outer$Inner$function$number в Kotlin и Outer$Inner$number в Java.

Это разница в компиляторе, которая приводит к различным именам; Java исключает функцию, где, как это делает Kotlin. Это имя файла .class, поэтому, если вы собираете свой проект и просматриваете имена файлов в проводнике для любой имеющейся ОС ( Не смотрите в IntelliJ . декомпилируйте файлы для вас. И помните, вы просто ищете имя, которое IntelliJ перепутает, объединяя файлы .class в один, чтобы соответствовать оригинальному источнику)


Так же, как заключительная мета, я печатаю класс вместо печати объекта. Интерфейсы не имеют переопределенного метода toString, что означает, что по умолчанию используется метод Object, который возвращает getClass().getName() + "@" + Integer.toHexString(hashCode()); (оригинальный код можно найти здесь ). println(this) аналогично println(this.toString()), который вызывает метод toString в Object, который печатает имя класса. println(this) - это то же самое, что печать объекта или печать класса

...