An interface declares abstract methods (no body, must be overridden), methods with a default body (inherited automatically unless overridden), and abstract properties (implementing classes must provide a value or accessor). Unlike Java, which added default methods only in Java 8, Kotlin interfaces have supported them since Kotlin's 1.0 release. A class implementing two interfaces that both provide a default for the same method must explicitly resolve the conflict itself, using super<InterfaceName>.method() to specify which one to call, rather than the ambiguity being silently resolved.
Default methods: real implementation, no override required
Person never implements greet() itself — it inherits Greeter's default body directly, exactly the kind of API-evolution flexibility Java interfaces couldn't offer until Java 8 added the same capability.
interface Greeter {
fun name(): String
fun greet() { // default implementation — a REAL method body
println("Hello, ${name()}!")
}
}
class Person(val personName: String) : Greeter {
override fun name() = personName
// no need to override greet() — the default is inherited
}
Person("Alice").greet() // "Hello, Alice!"Abstract properties: something Java interfaces historically couldn't do
An interface can require implementers to provide a property, not just a method — the implementing class supplies it either as a constructor parameter or a computed value.
interface Named {
val name: String // abstract property — must be provided by implementers
}
class Product(override val name: String) : NamedResolving a conflict between two default methods
When a class implements two interfaces that both supply a default for the SAME method, Kotlin requires the class to resolve the conflict explicitly — it will not silently pick one over the other.
interface A { fun greet() = println("Hello from A") }
interface B { fun greet() = println("Hello from B") }
class C : A, B {
override fun greet() {
super<A>.greet() // must explicitly pick — or call both, or write new logic
super<B>.greet()
}
}