Как работает Kotlin lambda по сравнению с анонимным внутренним классом? - PullRequest
0 голосов
/ 24 февраля 2019

Android Studio предлагает заменить анонимный внутренний класс на лямбду.

titleTextView.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
        Log.d("MY_TAG", "textView clicked in anonymous inner class")
    }
})

Декомпилированный код Java:

var10000 = this.titleTextView;
if (this.titleTextView == null) {
    Intrinsics.throwUninitializedPropertyAccessException("titleTextView");
}

var10000.setOnClickListener((OnClickListener)(new OnClickListener() {
    public void onClick(@Nullable View v) {
        Log.d("MY_TAG", "textView clicked in anonymous inner class");
    }
}));

Перед лямбда, чтобы избежать создания нового объекта для каждого из представлений, которыебыли установлены OnClickListener, было бы лучше, чтобы Activity / Fragment реализовывал View.OnClickListener интерфейс или использовал Butterknife @OnClick аннотацию.

Насколько отличается производительность лямбды, как показано ниже?

titleTextView.setOnClickListener { Log.d("MY_TAG", "textView clicked in lambda") }

Декомпилированный код Java:

TextView var10000 = this.titleTextView;
if (this.titleTextView == null) {
    Intrinsics.throwUninitializedPropertyAccessException("titleTextView");
}

var10000.setOnClickListener((OnClickListener)null.INSTANCE);

В случае лямбды я не вижу Log.d("MY_TAG", "textView clicked in lambda") в декомпилированном коде.

1 Ответ

0 голосов
/ 24 февраля 2019

Производительность лямбды будет, по крайней мере, такой же хорошей, как и при создании анонимного внутреннего класса.

  • Если он не захватывает какие-либо ссылки, компилятор сделает его одиночным внутрикакой бы класс он ни использовал в качестве меры оптимизации.Это то, что происходит в вашем случае, поскольку содержимое вашего слушателя не относится ни к чему, кроме лямбды.(Этот одноэлементный экземпляр - это то, к чему пытается обратиться null.INSTANCE, декомпилятор просто испытывает проблемы с разрешением имени класса, сгенерированного для лямбды.) Так что в этом случае стоимость лямбды составляет всего 1 выделение объекта.

  • Если ваша лямбда-то захватывает что-то, например, вот так:

    val random = Random().nextInt()
    titleTextView.setOnClickListener { 
        Log.d("MY_TAG", "textView clicked in lambda, random value was $random") 
    }
    

    ... тогда всякий раз, когда вы устанавливаете слушателя, должен быть выделен новый экземплярпотому что эти экземпляры должны хранить ссылки на переменные, которые могут отличаться при каждом создании.В этом случае вы получаете столько распределений объектов, сколько раз запускаете метод лямбды. Обратите внимание, что если это делается только во время установки, как в onCreate, это будет означать только 1 выделение объектов.

Таким образом, вы можете получить:

  • 0 дополнительных выделений, если ваши слушатели являются методами вашего уже существующего класса (Fragment или Activity).
  • 1дополнительное распределение, если вы используете лямбду, которая ничего не захватывает.
  • N дополнительные выделения, если вы используете лямбду, которая захватывает что-то, N - это количество раз, когда вы запускаете код, где используется лямбда, что можетбудет 1, если это только во время инициализации.
  • N дополнительных выделений, если вы используете анонимный внутренний класс, так как там нет оптимизации для классов без захвата.Опять же, это может быть на самом деле 1. Во многих случаях это может быть 1.

Даже если использование методов внутри существующего класса будет означать 0 дополнительных выделений, я бы не стал использовать этот подход для повышения производительности - любое повышение будетвероятно, будет совершенно незаметным.Вместо этого используйте более удобочитаемое и удобное для вас решение.

...