为何在Kotlin中优先使用by委托而非匿名子类?
Great question! Let’s break down the strengths of each approach you’ve outlined—both solve the problem of skipping boilerplate when you only care about one interface method, but they bring distinct benefits depending on your context.
Using Kotlin's by Delegation (The Google I/O 2017 Example)
This approach leverages Kotlin’s built-in delegation feature, and here’s why it shines:
- No open class requirement: Your
EmptyTransitionListeneris a singletonobject, so you don’t need to mark the class asopento allow extension. This keeps your code more encapsulated—you don’t have to loosen access rules just to let other classes reuse the empty implementation. - Avoids inheritance limits: Kotlin (like Java) only allows single class inheritance. If your
MyListenerever needs to extend another class (say, a custom base listener), delegation is the only way to handle theTransitionListenerinterface without hitting that limit. You can delegate to multiple interfaces, but you can’t extend multiple classes. - Singleton efficiency: The empty listener singleton is reused across all instances of
MyListener, so you don’t create unnecessary duplicate objects every time you need a listener. It’s a small optimization, but it adds up if you’re creating many listeners throughout your app. - Clear, explicit intent: The
bykeyword makes it immediately obvious to anyone reading the code that all unimplementedTransitionListenermethods are delegated to the empty singleton. There’s no ambiguity about where the default behavior comes from.
Here’s the code again for reference:
class MyListener : TransitionListener by EmptyTransitionListener { override fun onTransitionStart(transition: Transition) { } } object EmptyTransitionListener : TransitionListener { override fun onTransitionEnd(transition: Transition) {} override fun onTransitionResume(transition: Transition) {} override fun onTransitionPause(transition: Transition) {} override fun onTransitionCancel(transition: Transition) {} override fun onTransitionStart(transition: Transition) {} } window.sharedElementEnterTransition.addListener(MyListener())
Using Inheritance of an Empty Base Class (Your Usual Approach)
This classic adapter pattern is a staple in Java and Android development, and it has its own advantages:
- Familiarity for cross-language teams: If your team includes Java developers, this pattern is instantly recognizable (think Android’s
View.OnClickListeneradapters). They won’t need to learn Kotlin’s delegation syntax to understand what’s happening. - Simpler incremental changes: If you later decide to override more than just
onTransitionStart, you can add anotheroverridemethod directly to the anonymous inner class without modifying the baseEmptyTransitionListeneror changing your overall approach. It’s a low-friction way to adapt your code as needs change. - Lower learning curve for Kotlin newcomers: Delegation is a powerful Kotlin-specific feature, but it can feel abstract to someone new to the language. The inheritance approach relies on basic OOP concepts that most developers already know, making it easier to pick up quickly.
Here’s your reference code:
open class EmptyTransitionListener : TransitionListener { override fun onTransitionEnd(transition: Transition) {} override fun onTransitionResume(transition: Transition) {} override fun onTransitionPause(transition: Transition) {} override fun onTransitionCancel(transition: Transition) {} override fun onTransitionStart(transition: Transition) {} } window.sharedElementEnterTransition.addListener(object: EmptyTransitionListener() { override fun onTransitionStart(transition: Transition) { } })
Quick Recommendation
If you’re working in a Kotlin-first codebase and want to avoid unnecessary inheritance (or need to work around single inheritance constraints), go with the by delegation approach. If you’re on a mixed Java/Kotlin team or prefer a more familiar, straightforward pattern, the inheritance method is a solid choice—both get the job done!
内容的提问来源于stack exchange,提问作者Benjamin

