Why does the below code compile?
Inside SomeScope.getEntity():
get<Entity>() clearly violates the bounds of the get() function.
But if you omit <Entity> (which you can because of return type inference) the code compiles.
interface EntityBase
interface Entity
class SomeScope {
inline fun <reified T : EntityBase> get(): T {
return "whatever" as T // doesn't matter
}
}
fun SomeScope.getEntity(): Entity {
return get() // get<Entity>() wouldn't compile
}
Related
Is it impossible to use generic on interface level as argument type for function?
I read about out and in keywords but as I understand they don't work for this case.
interface BaseB
open class ChildB1: BaseB
open class ChildB2: BaseB
abstract class BaseMapper<V: BaseB> {
open fun test(v: V) {
return
}
}
class TestMapper1: BaseMapper<ChildB1>() {
override fun test(v: ChildB1) {
return
}
}
class TestMapper2: BaseMapper<ChildB2>() {
override fun test(v: ChildB2) {
return
}
}
#Test
fun t() {
//ERROR
val mappers: List<BaseMapper<BaseB>> = listOf(TestMapper1(), TestMapper2())
mappers[0].test(ChildB1())
}
A BaseMapper<ChildB1> is not logically a BaseMapper<BaseB>. It consumes ChildB’s, so if you passed some other implementation of Base it would cause a ClassCastException if the compiler let you do that. There is no common subtype of your two subclasses besides Nothing, so the only way to put both of these types in the same list is to make it a List<BaseMapper<in Nothing>>.
Example of why it is not logically a BaseMapper<BaseB>:
open class ChildB1: BaseB {
fun sayHello() = println("Hello world")
}
class TestMapper1: BaseMapper<ChildB1>() {
override fun test(v: ChildB1) {
v.sayHello() // if v is not a ChildB1, this would be impossible
}
}
//...
val impossibleCast: BaseMapper<BaseB> = TestMapper1()
// TestMapper1 cannot call sayHello() because it's undefined for ChildB2.
// This is impossible:
impossibleCast.test(ChildB2())
// ...so the compiler prevents you from doing the impossible cast in the first place.
interface Foo<T: Bar> {
fun example(bar: T)
}
interface Bar
class Bar1 : Bar
class Bar2 : Bar
class FooEx1 : Foo<Bar1> {
override fun example(bar: Bar1) { }
}
class FooEx2 : Foo<Bar2> {
override fun example(bar: Bar2) { }
}
// Won't compile
// Even though FooEx1 and FooEx2 *are* Foo<Bar>
class ExampleDoesntCompile {
val collection = mutableListOf<Foo<Bar>>().apply {
this.add(FooEx1())
this.add(FooEx2())
}
}
// Will compile
// But have to cast FooEx1 and FooEx2 to Foo<Bar>
class ExampleDoesCompileButRequiresCast {
val collection = mutableListOf<Foo<Bar>>().apply {
this.add(FooEx1() as Foo<Bar>)
this.add(FooEx2() as Foo<Bar>)
}
}
So, I could for instance, state that Foo's parameterized type is out, but then I get a compile error for the function example:
interface Foo<out T: Bar> {
fun example(bar: T)
}
Error: Type parameter T is declared as 'out' but occurs in 'in' position in type T
Because generic types in Java / Kotlin are invariant by default. variance
interface Foo<out T: Bar>
If you can't make it covariant, then make the list items covariant
val collection = mutableListOf<Foo<out Bar>>().apply {
this.add(FooEx1())
this.add(FooEx2())
}
//or val collection = mutableListOf(FooEx1(), FooEx2())
So it'll crash at run time with the cast?
Here is example code that would crash:
val foo: Foo<Bar> = collection[0]
foo.example(Bar2())
So if you could create collection without a cast as in your ExampleDoesntCompile, you'd get code without any casts which throws a ClassCastExcepion.
This also shows why the interface can't be declared with out:
val foo: Foo<Bar> = FooEx1() // out would make this legal
foo.example(Bar2())
It would make sense to declare your interface with in, but this would mean a Foo<Bar> is a Foo<Bar1> and a Foo<Bar2>, not vice versa, so still wouldn't let you put FooEx1/2 into a collection of Foo<Bar>s.
Having a java class, using androidStudio to translate to kotlin.
Got a error and not sure how to correctly translate it.
The java code:
public class BaseDataImpl extends BaseData {
private final BaseData[] translators;
public BaseDataImpl(final BaseData... translators) {
this.translators = cloneArray(translators);
}
public static <T> T[] cloneArray(final T[] array) {
if (array == null) {
return null;
}
return array.clone();
}
}
after the code translation, got error: required Array<BaseData>?, found Array<out BaseData>, but the translators in the cloneArray<BaseData>(translators) call is defined as val translators: Array<BaseData>?,
anyone could help to explain?
class BaseDataImpl(vararg translators: BaseData) : BaseData() {
private val translators: Array<BaseData>?
init {
this.translators = cloneArray<BaseData>(translators) //<=== error: required Array<BaseData>?, found Array<out BaseData>
}
companion object {
fun <T> cloneArray(array: Array<T>?): Array<T>? {
return array?.clone()
}
}
}
It is written in the Kotlin function reference regarding varargs:
Inside a function a vararg-parameter of type T is visible as an array of T, i.e. the ts variable in the example above has type Array<out T>.
where the referenced function was:
function <T> asList(vararg ts: T): List<T>
So in your case you actually pass an Array<out BaseData> but you only accept an array of type Array<T>? (in your case Array<BaseData>). Either you adapt all of the types to Array<out T> (which basically is similar as saying List<? extends BaseData> in Java) or you take care that you are only dealing with Ts instead, e.g. with:
inline fun <reified T> cloneArray(array: Array<out T>?): Array<T>? {
return array?.clone()?.map { it }?.toTypedArray()
}
But look up the documentation regarding this: Kotlin generics reference - type projections. You probably can accomplish this even easier.
I'm trying to implement an abstract class in kotlin which extends a MultiValuedMap, when I was trying to override keySet() method, I got the error
platform declaration clash: The following declarations have the same JVM signature (keySet()Ljava/util/Set;)
My code:
abstract class ConfigProperties<K, V>(delegate: Map<K, V>?): MultivaluedMap<String, String> {
protected val delegate: Map<K, V>
init {
if (delegate == null) {
throw NullPointerException("Config properties delegate must not be null.")
}
this.delegate = delegate
}
abstract fun putCacheProperty(key: Parameter, value: Any)
abstract fun getCacheProperty(key: Parameter): Any
protected val UNSUPPORTED_MESSAGE = "ConfigProperties is immutable."
override fun keySet(): Set<String> {
return delegate.keys
}
}
Any hint to solve this? Thanks!
I think your problem begins with MultivaluedMap<String,String>
abstract class ConfigProperties<K, V>(delegate: Map<K, V>?):
MultivaluedMap<String, String> { ... }
Overlook the String type parameter for the moment. MultivaluedMap<K,V> is an interface that has the Map<K,List<V>> super interface. But in your code, you have a delegate of type Map<K,V>. You try to override a the setKey member of the Map<K,List<V>> super interface by returning delegate.keys which is not the same as Map<K,List<V>>.keys (i.e., whose member you are overriding).
So, you can try the following...
abstract class ConfigProperties<K, V>(delegate: Map<K, V>?):
MultivaluedMap<K, V> {
protected val delegate: Map<K, List<V>>
init {
if (delegate == null) {
throw NullPointerException("Config properties delegate must not be null.")
}
this.delegate = delegate
}
abstract fun putCacheProperty(key: Parameter, value: Any)
abstract fun getCacheProperty(key: Parameter): Any
protected val UNSUPPORTED_MESSAGE = "ConfigProperties is immutable."
override fun keySet(): Set<K> {
return delegate.keys
}
}
As for the String type parameter, did you mean to use K,V? Whatever you meant, you will need to make the consistent.
Is there a way to specify the return type of a function to be the type of the called object?
e.g.
trait Foo {
fun bar(): <??> /* what to put here? */ {
return this
}
}
class FooClassA : Foo {
fun a() {}
}
class FooClassB : Foo {
fun b() {}
}
// this is the desired effect:
val a = FooClassA().bar() // should be of type FooClassA
a.a() // so this would work
val b = FooClassB().bar() // should be of type FooClassB
b.b() // so this would work
In effect, this would be roughly equivalent to instancetype in Objective-C or Self in Swift.
There's no language feature supporting this, but you can always use recursive generics (which is the pattern many libraries use):
// Define a recursive generic parameter Me
trait Foo<Me: Foo<Me>> {
fun bar(): Me {
// Here we have to cast, because the compiler does not know that Me is the same as this class
return this as Me
}
}
// In subclasses, pass itself to the superclass as an argument:
class FooClassA : Foo<FooClassA> {
fun a() {}
}
class FooClassB : Foo<FooClassB> {
fun b() {}
}
You can return something's own type with extension functions.
interface ExampleInterface
// Everything that implements ExampleInterface will have this method.
fun <T : ExampleInterface> T.doSomething(): T {
return this
}
class ClassA : ExampleInterface {
fun classASpecificMethod() {}
}
class ClassB : ExampleInterface {
fun classBSpecificMethod() {}
}
fun example() {
// doSomething() returns ClassA!
ClassA().doSomething().classASpecificMethod()
// doSomething() returns ClassB!
ClassB().doSomething().classBSpecificMethod()
}
You can use an extension method to achieve the "returns same type" effect. Here's a quick example that shows a base type with multiple type parameters and an extension method that takes a function which operates on an instance of said type:
public abstract class BuilderBase<A, B> {}
public fun <B : BuilderBase<*, *>> B.doIt(): B {
// Do something
return this
}
public class MyBuilder : BuilderBase<Int,String>() {}
public fun demo() {
val b : MyBuilder = MyBuilder().doIt()
}
Since extension methods are resolved statically (at least as of M12), you may need to have the extension delegate the actual implementation to its this should you need type-specific behaviors.
Recursive Type Bound
The pattern you have shown in the question is known as recursive type bound in the JVM world. A recursive type is one that includes a function that uses that type itself as a type for its parameter or its return value. In your example, you are using the same type for the return value by saying return this.
Example
Let's understand this with a simple and real example. We'll replace trait from your example with interface because trait is now deprecated in Kotlin. In this example, the interface VitaminSource returns different implementations of the sources of different vitamins.
In the following interface, you can see that its type parameter has itself as an upper bound. This is why it's known as recursive type bound:
VitaminSource.kt
interface VitaminSource<T: VitaminSource<T>> {
fun getSource(): T {
#Suppress("UNCHECKED_CAST")
return this as T
}
}
We suppress the UNCHECKED_CAST warning because the compiler can't possibly know whether we passed the same class name as a type argument.
Then we extend the interface with concrete implementations:
Carrot.kt
class Carrot : VitaminSource<Carrot> {
fun getVitaminA() = println("Vitamin A")
}
Banana.kt
class Banana : VitaminSource<Banana> {
fun getVitaminB() = println("Vitamin B")
}
While extending the classes, you must make sure to pass the same class to the interface otherwise you'll get ClassCastException at runtime:
class Banana : VitaminSource<Banana> // OK
class Banana : VitaminSource<Carrot> // No compiler error but exception at runtime
Test.kt
fun main() {
val carrot = Carrot().getSource()
carrot.getVitaminA()
val banana = Banana().getSource()
banana.getVitaminB()
}
That's it! Hope that helps.
Depending on the exact use case, scope functions can be a good alternative. For the builder pattern apply seems to be most useful because the context object is this and the result of the scope function is this as well.
Consider this example for a builder of List with a specialized builder subclass:
open class ListBuilder<E> {
// Return type does not matter, could also use Unit and not return anything
// But might be good to avoid that to not force users to use scope functions
fun add(element: E): ListBuilder<E> {
...
return this
}
fun buildList(): List<E> {
...
}
}
class EnhancedListBuilder<E>: ListBuilder<E>() {
fun addTwice(element: E): EnhancedListBuilder<E> {
addNTimes(element, 2)
return this
}
fun addNTimes(element: E, times: Int): EnhancedListBuilder<E> {
repeat(times) {
add(element)
}
return this
}
}
// Usage of builder:
val list = EnhancedListBuilder<String>().apply {
add("a") // Note: This would return only ListBuilder
addTwice("b")
addNTimes("c", 3)
}.buildList()
However, this only works if all methods have this as result. If one of the methods actually creates a new instance, then that instance would be discarded.
This is based on this answer to a similar question.
You can do it also via extension functions.
class Foo
fun <T: Foo>T.someFun(): T {
return this
}
Foo().someFun().someFun()