I am struggling to understand how generics / type parameters work in Kotlin. I am working on a (fairly complex) app that is throwing some very confusing error messages during compilation. I've simplified things below to the minimum amount of code that will reproduce the error.
I have an interface and two abstract classes:
interface Player {
fun play()
}
abstract class Device <T : Player> {
abstract fun getPlayer(): T
}
abstract class DeviceFactory {
abstract fun <T : Player> create(): Device<T>
}
The problem arises when I try to create a class that implements DeviceFactory:
class MyDeviceFactory : DeviceFactory() {
class MyPlayer : Player {
override fun play() {
println("[sound plays here]")
}
}
class MyDevice : Device<MyPlayer>() {
override fun getPlayer() = MyPlayer()
}
override fun create() = MyDevice()
}
The last line of code is where the problem arises, yielding the following error message:
Conflicting overloads: public open fun create(): MyDeviceFactory.MyDevice defined in MyDeviceFactory,
public abstract fun create(): Device defined in DeviceFactory
Thinking that maybe the problem was the missing type parameter, I tried this instead:
override fun <T : Player> create() = MyDevice()
Now I have a different error message:
Return type of 'create' is not a subtype of the return type of the overridden member
'public abstract fun create(): Device defined in DeviceFactory'
This doesn't make sense — MyDevice is a subtype of Device<T>, right? To be sure, I tried making the return type explicit:
override fun <T : Player> create(): Device<T> = MyDevice()
No dice:
Type mismatch: inferred type is MyDeviceFactory.MyDevice but Device was expected
How can I create a class that derives from DeviceFactory and returns an instance of MyDevice?
You need to declare the type for DeviceFactory on it's class:
abstract class DeviceFactory<T : Player> {
abstract fun create(): Device<T>
}
Then you can define a factory that returns a concrete Player:
class MyDeviceFactory : DeviceFactory<MyPlayer>() {
override fun create(): Device<MyPlayer> = MyDevice()
}
Consider the following example: I have an abstract class for Animal, and every animal has a mouth, but because every animal's mouth is different, the mouth class is also abstract:
abstract class Animal {
var numberLegs: Int = 4
var mouth: Mouth? = null
}
abstract class Mouth {
abstract fun makeSound()
}
I can now create a Dog and a DogMouth:
class Dog: Animal() {
override var mouth: Mouth = DogMouth()
}
class DogMouth: Mouth() {
override fun makeSound() {
println("Bark!")
}
}
But this allows me to also assign other types of mouths to the dog, which I don't want, e.g.:
class CatMouth: Mouth() {
override fun makeSound() {
println("Meow!")
}
}
fun main() {
val dog = Dog()
dog.mouth.makeSound() // will print "Bark!"
dog.mouth = CatMouth() // I don't want this to work
dog.mouth.makeSound() // will print "Meow!"
}
And setting override var mouth: DogMouth = DogMouth() doesn't work.
How can I make sure that Dogs only have DogMouths (and other dog body parts)?
Similar problems are addressed here and here.
The solution is to use a generic parameter:
abstract class Animal<MouthType: Mouth> {
var numberLegs: Int = 4
abstract var mouth: MouthType
}
class Dog: Animal<DogMouth>() {
override var mouth: DogMouth = DogMouth()
}
This makes dog.mouth = CatMouth() fail with a type mismatch.
With more body parts extra generics need to be added:
abstract class Animal<MouthType: Mouth, EarType: Ear, TailType: Tail> {
var numberLegs: Int = 4
abstract var mouth: MouthType
abstract var ear: EarType
abstract var tail: TailType
}
Is it possible to call a function from a child type from a constructor? Please take a look at the example
class Dog(animalType: DogType) : Animal(animalType) {
fun doSomething() {
animalType.runDogTypeFunction() // error but animalType is always DogType
}
}
abstract class Animal(val animalType: AnimalType)
interface AnimalType
enum class DogType() : AnimalType {
DOG1, DOG2;
fun runDogTypeFunction() {}
}
enum class CatType() : AnimalType {
CAT1, CAT2;
fun runCatTypeFunction() {}
}
animalType is const (val) so it always is DogType. I do not understand why I cannot call a method from the DogType class.
I tried to override val but I received NPE
Your property is declared in the base Animal class (as AnimalType). The constructor param in Dog doesn't exist by the time you call doSomething.
You could try something like this:
abstract class Animal<T: AnimalType>(val animalType: T)
class Dog(animalType: DogType) : Animal<DogType>(animalType) {
fun doSomething() {
animalType.runDogTypeFunction()
}
}
I know most of you already know of the animal-cow-grass-food problem
-which states that you have a code like below, that has a type constraint on Cow (which inherits Animal ) to only eat SuitableFood (which inherits Food )
Below is the SCALA representation of the same
class Food
class Grass extends Food
class Cookies extends Food
class Fish extends Food
abstract class Animal {
type SuitableFood <: Food
def eat(food: SuitableFood)
}
class Cow extends Animal {
type SuitableFood = Grass
override def eat(food: SuitableFood) = {}
}
val bessy: Animal = new Cow
bessy eat new Fish
bessy eat new Cookies
I was wondering if similar is possible in KOTLIN or JAVA ?
Not sure what you want to achieve. Restriction for Cow to eat only Grass ?
I think this can be done through generic types.
abstract class Food
open class Grass : Food()
class GreenGrass : Grass()
class Fish : Food()
abstract class Animal<T : Food> {
fun eat(food: T) { ... }
}
class Cow : Animal<Grass>()
class Bear : Animal<Fish>()
class Test {
fun test() {
val cow = Cow()
cow.eat(Grass()) // ok
cow.eat(GreenGrass()) // ok
cow.eat(Fish()) // not ok
val bear = Bear()
bear.eat(Fish()) // ok
bear.eat(Grass()) // not ok
}
}
With the code below, I am getting the following error in IntelliJ IDEA 13.1.6 and Kotlin plugin 0.11.91.AndroidStudio.3:
Platform declaration clash: The following declarations have the same JVM signature (getName()Ljava/lang/String;):
• public open fun getName(): kotlin.String?
• internal final fun <get-name>(): kotlin.String?
Java class, JavaInterface.java:
public interface JavaInterface {
public String getName();
}
Kotlin class, KotlinClass.kt
public class KotlinClass(val name: String?) : JavaInterface
I've tried overriding the 'getter' method by
adding override fun getName(): String? = name, but that produces the same error.
I can see one workaround by doing this instead:
public class KotlinClass(val namePrivate: String?) : JavaInterface {
override fun getName(): String? = namePrivate
}
But in my real-world case I have a number of properties to implement and need setters too. Doing this for each property doesn't seem very Kotlin-ish. What am I missing?
Making that variable private solves the problem.
public class KotlinClass(private val name: String?) : JavaInterface
You could use #JvmField for instructs the compiler not generate getter/setter, and you can implement your setters and getters. With this your code work well in Java (as attribute getter/setter) and Kotlin as property
Example:
JAVA:
public interface Identifiable<ID extends Serializable>
{
ID getId();
}
KOTLIN:
class IdentifiableImpl(#JvmField var id: String) :Identifiable<String>
{
override fun getId(): String
{
TODO("not implemented")
}
}
The annotation feature of Kotlin named #JvmName will solve the duplication problem in Java and Kotlin when having the same signature.
fun function(p: String) {
// ...
}
// Signature: function(Ljava/lang/String)
With the use of JvmName will be:
#JvmName("functionOfKotlin")
fun function(p: String) {
// ...
}
// Signature: functionOfKotlin(Ljava/lang/String)
IMHO most readable combination is field + explicit interface implementation by the single-expression function (combination of #Renato Garcia's and #Steven Spungin's answers):
Java:
public inteface SomeInterface {
String getFoo();
}
Kotlin:
class Implementation(#JvmField val foo: String) : SomeInterface {
override fun getFoo() = foo
}
Another work-around is to declare the properties in an abstract Kotlin class, then write a small java class that extends KotlinClass and implements JavaInterface.
// JavaInterface.java
public interface JavaInterface {
int getFoo();
void setFoo(int value);
}
// KotlinClass.kt
abstract class KotlinClass(open var foo : Int = 0) {
}
// JavaAdapter.java
class JavaAdapter extends KotlinClass implements JavaInterface {
// all code in KotlinClass, but can't implement JavaInterface there
// because kotlin properties cannot override java methods.
}
We have found that to use the same names without clashing, the ctor args must be private AND you must still override the interfaces methods. You don't need any additional backing fields. Also, your expression body assignment will not recurse, so you can safely use that syntax.
Java Interface
interface IUser {
String getUserScope();
String getUserId();
}
Kotlin Class
class SampleUser(private val userScope: String, private val userId: String) : IUser {
override fun getUserId() = userId
override fun getUserScope() = userScope
}
If you have direct control over the interface then the best approach is to write the interface in Kotlin. You can then write your class
public class KotlinClass(override val name: String?) : KotlinInterface
and still reference it from any Java code using the same interface as before. This looks a lot neater than setting all the properties to private and overriding the get function. Obviously if you can't migrate the interface to Java because you don't own it then that seems to be the only solution.
public interface JavaInterface {
public String getName();
}
public class KotlinClass(val namePrivate: String?) : JavaInterface {
private var name = namePrivate
override fun getName(): String? {
return name
}
}
Rename the variable to something else, or make it private if u dont want it to be public.
convert function to property instead of initializing property from a function.
for ex:
fun getCountriesList(): List<Country> {
val countries = mutableListOf<Country>()
countries.add(Country("in", "+91", "India", R.drawable.indian_flag))
countries.add(Country("us", "+1", "United States",R.drawable.us_flag))
return countries
}
to
val countriesList: List<Country>
get() {
val countries = mutableListOf<Country>()
countries.add(Country("in", "+91", "India", R.drawable.indian_flag))
countries.add(Country("us", "+1", "United States", R.drawable.us_flag))
return countries
}