If I have the following code:
fun main() {
println(Example("test", 1))
}
class Example(private val text: String, private val num: Int) {
override fun toString(): String {
return "String: $text $num"
}
}
and it works.
Is there any way to do this for types different from string?
Example:
class Example {
fun toList(): List {
//Convert to list...
}
}
thisFunctionRequireList(Example2())
I will first explain how
println(Example1())
works, and from there, create something similar.
First, note that println has many overloads. This println call does not call the println(String) overload. It calls the println(Any?) overload.
The println(Any?) overload is implemented to transform the Any parameter into a String using toString, and then calls the println(String) overload using that string. Here is the Kotlin/Native implementation:
public actual fun println(message: Any?) {
println(message.toString())
}
Note that it is able to do this, only because Any?, and by extension Any, declares the toString method. This allows it to convert any value you pass, to a String. At runtime, this gets dispatched to the toString override that you declared in Example1.
Therefore, it is not because you overrode toString, that you were able to call println with Example. It is because
println has an overload that takes Any, allowing you to pass anything into it
Any also declares a toString that you can override, which println(Any) also uses in its implementation
Any does not declare a toList method, so we can't use Any if we want to do the same for List.
We can declare a new interface for types that can be converted to lists:
interface ConvertibleToList<T> {
fun toList(): List<T>
}
This is like an Any, but only for types that implement it. Example2 can then implement the interface:
class Example2: ConvertibleToList<String> {
override fun toList(): List<String> {
//Convert to list...
}
}
Now suppose there is an existing function that takes a List<String>:
fun doThingsToStrings(strings: List<String>) { ... }
You can add a new overload of this, with the parameter type being ConvertibleToList<T>:
fun doThingsToStrings(strings: ConvertibleToList<String>) = doThingsToStrings(strings.toList())
doThingsToStrings becomes just like println, with one overload taking the "exact" type (List<String>), and another overload taking types that can be converted to the "exact" type (ConvertibleToList<String>).
Now you can call doThingsToStrings with Example2()!
Related
I'm trying to create an AnimalFactory that returns generic factories for making different types of Animals, depending on the arguments passed to the AnimalFactory.
Here's the code:
interface Animal {
fun talk(): String
}
class Cow: Animal {
override fun talk(): String {
return "mooo"
}
}
class Cat: Animal {
override fun talk(): String {
return "miow"
}
}
class Dog: Animal {
override fun talk(): String {
return "bark"
}
}
object AnimalFactory {
fun <T: Animal> AnimalMakerFactory(type: String): AnimalMaker<T> {
val maker = when (type) {
"cat" -> CatMaker()
"dog" -> DogMaker()
else -> CowMaker()
}
return maker
}
}
interface AnimalMaker<out T: Animal> {
fun make(): T
}
class CatMaker: AnimalMaker<Cat> {
override fun make(): Cat {
return Cat()
}
}
class DogMaker: AnimalMaker<Dog> {
override fun make(): Dog {
return Dog()
}
}
class CowMaker: AnimalMaker<Cow> {
override fun make(): Cow {
return Cow()
}
}
I get a type exception:
Type mismatch.
Required: AnimalMaker<T>
Found: AnimalMaker<Animal>
I thought that AnimalMaker would solve this, but apparently not. Why is AnimalMaker<T> not of type AnimalMaker<Animal> here?
The return value of the function is AnimalMaker<T> and not AnimalMaker<Animal> because that’s what you declared as the return type. The variable maker is indeed an AnimalMaker<Animal> but that isn’t a match for what the function is supposed to return because T could be a subtype of Animal.
You declared your function as having a generic type of T: Animal. Generic types are always an input to the function. In this case, it doesn’t make sense to use a generic input to the function because there’s no way to enforce that the type given is a match for the input String it corresponds with. To make your function work, you can remove <T : Animal and declare that it returns AnimalMaker<Animal>.
A little more explanation. There are two reasons why you might want to use generics in a function signature.
Enforce input parameter types.
Determine the output type.
You might use generics for one or both reasons (but the second can only be done by itself in a useful way by using reified generics, except in very specific cases where the returned class won’t be producing anything).
In your case, your input generic is not used to enforce the input parameter since that is just a String. To use it for the second reason, you would have to cast your return value’s type to the unknown (to the compiler) type T which would be unsafe because there’s no way to know if the input type given at the call site is a valid match for the given input String. And if you expected the call site to pass the right type, it would be redundant and error prone to also require a matching String to be passed.
Edit:
If you know the input type at compile time, then you can do this with reified generics. Get rid of the String input. It would look like this:
object AnimalFactory {
inline fun <reified T: Animal> AnimalMakerFactory(): AnimalMaker<T> {
#Suppress("UNCHECKED_CAST")
return when (T::class) {
Cat::class -> CatMaker()
Dog::class -> DogMaker()
Cow::class -> CowMaker()
else -> error("No factory found for type ${T::class}.")
} as AnimalMaker<T>
}
}
// Example usage
val someCatFactory = AnimalFactory.AnimalFactoryMaker<Cat>()
val cat: Cat = someCatFactory.make()
Inside this function, it is up to you to match the types up correctly, or there will be a ClassCastException at runtime. It seems logically it should be able to automatically cast them, but the compiler isn't sophisticated enough (yet?).
If I have a following interface:
interface BaseDataRemote<T, in Params> {
fun getData(params: Params? = null): Single<T>
}
Would it be possible have implementation of this interface that does not take Params?
To have effectively something like:
interface BaseDataRemote<T> {
fun getData(): Single<T>
}
Implementation is as follows:
class RemoteSellerDataSource #Inject constructor(
private val sellerApi: SellerApi,
#Named("LANG") private val lang: String
) : BaseDataRemote<SellerEntity, Nothing> {
override fun getData(params: Nothing?): Single<SellerEntity> {
return sellerApi.getSeller(lang).map { it.fromApiEntity() }
}
}
I use Dagger 2 to module to bind this implementation:
#Module
internal interface RemoteModule {
#Binds
#CoreScope
fun bindsSellerRemote(remoteSellerDataSource: RemoteSellerDataSource): BaseDataRemote<SellerEntity, Nothing>
}
I tried using Nothing as second type parameter, but it does not seem to work
(I'm getting required: class or interface without bounds error
Full error message:
RemoteSellerDataSource.java:6: error: unexpected type
public final class RemoteSellerDataSource implements com.bigchangedev.stamps.business.sdk.data.base.data.BaseDataRemote<SellerEntity, ?> {
^
required: class or interface without bounds
found:?
Thanks.
EDIT: the original answer was a pure Kotlin answer because the OP didn't mention Dagger.
Using Nothing is correct and works in pure Kotlin. However, Dagger seems to convert your code to Java, and in doing so it uses wildcards for the generics (which it doesn't like because it wants exact type matches). To avoid this issue, you can try using #JvmSuppressWildcards on your generic type parameters:
class RemoteSellerDataSource #Inject constructor(
private val sellerApi: SellerApi,
#Named("LANG") private val lang: String
) : BaseDataRemote<SellerEntity, #JvmSuppressWildcards Nothing> {
override fun getData(params: Nothing?): Single<SellerEntity> {
return sellerApi.getSeller(lang).map { it.fromApiEntity() }
}
}
Although I'm not sure what will happen in Java with Nothing in that case. I guess this should have the same effect on the Java code as removing the in variance for the second type param in the interface declaration, but without weakening your Kotlin types.
Another workaround would be to use Unit instead of Nothing, which Dagger will most likely convert to Void in this case. This is not great for your types, though.
Original answer:
You can technically already call getData() without arguments thanks to the default value. An implementation that doesn't care about the params argument can simply expect null all the time.
The Kotlin type that only contains null and no other value is technically Nothing?, and since getData is defined with Params? (note the ?) as input, it should be correct to specify Nothing (even without ?) as second type argument. So you should be able to define an implementation like this:
interface BaseDataRemote<T, in Params> {
fun getData(params: Params? = null): Single<T>
}
class ImplementationWithoutParams<T> : BaseDataRemote<T, Nothing> {
override fun getData(params: Nothing?): Single<T> {
// params will always be null here
}
}
To avoid confusion for the users, this implementation may additionally provide a getData() method without arguments at all:
class ImplementationWithoutParams<T> : BaseDataRemote<T, Nothing> {
override fun getData(params: Nothing?): Single<T> = getData()
fun getData(): Single<T> {
TODO("implementation")
}
}
With reified, I could replace the below function
private fun <T>createFragmentX(fragmentClass: Class<T>, fragmentArgs: Bundle?): Fragment {
try {
val fragment = fragmentClass.newInstance() as Fragment
fragment.arguments = fragmentArgs
return fragment
} catch (exception: Exception) {
throw RuntimeException(exception.message)
}
}
with this
private inline fun <reified T>createFragmentZ(fragmentArgs: Bundle?): Fragment {
try {
val fragment = T::class.java.newInstance() as Fragment
fragment.arguments = fragmentArgs
return fragment
} catch (exception: Exception) {
throw RuntimeException(exception.message)
}
}
It looks to me, with reified there's no longer need of using Class or KClass. Am I right?
Is there other use of Class or KClass that can't be fulfilled by reified, other than wanted to do pure reflection work?
Short answer:
In your example, it does not make a difference, except for aesthetics.
Long answer:
If you pass in T as reified, you can retrieve the KClass from the reified type parameter T like this
val kClass = T::class
at any time. You then have all the options you would have using a KClass as parameter, so no difference here.
There are situations where you need to use KClass as parameter though. Here a two:
1) No inline
When you don't want your function to be inline, because using a reified type works only with inlined functions.
2) Default values
Another situation would be default values:
fun f(kClass: KClass<*> = Int::class) { /*...*/ }
You couldn't achieve exactly the same with a reified type parameter.
Conclusion:
In all other cases go for the generic type parameter, because it makes the call-site more concise as shown here (which may be personal preference):
inline fun <reified T> printType() {
println(T::class.simpleName)
}
fun printType(kClass: KClass<*>) {
println(kClass.simpleName)
}
Call-site
printType<Int>() // better
printType(Int::class)
You don't need to pass it as a parameter here, but your code still uses Class: that's what T::class.java is. So yes, there are many cases where you need Class and yours is one of them.
I am having some issues with Mockito and stubbing out methods that uses a class as a parameter in Kotlin.
I have a few classes defined as follows:
open interface interfaceFile {
fun someFun(param1: String): String
}
abstact class abstractClass {
abstract val variable1: RandomType
open fun<T> getObject(param1: String, param2: Class<T>, vararg param3: Any): T? {
doSomeStuff()
}
open class concreteClass #Autowired constructor(
override val variable1: RandomType
): abstractClass(), interfaceFile {
override fun someFun(param1: String): String {
return getObject(param1, string::Class.java)!!
}
Then I tried to use mockito to mock the getObject function and test it as follows:
fun setUp() {
MockitoAnnotations.initMock(this)
testObject = mock(concreteClass::class.java)
}
fun testSomeFun() {
`when`(testObject!!.getObject("string1", String::class.java)).thenReturn("Mocked")
val actualResponse = testObject!!.someFun("string1")
assertEquals("message", "Mocked", actualResponse)
}
In short, concreteClass.someFun will call the abstractClass.getObject which will call doSomeStuff. But during testing I want to stub out abstractClass.getObject to just return. But the current behavior seems to be that I always get null instead.
I've simplified it to the point where it takes a single string parameter and stubbing it out then works, but seems when I introduce the class type as a parameter it stops working.
Search around it seems like my issue has to do with incorrect argument matching in my when call, but I can't figure out whats wrong with it since I even put in exact values instead of using ArgumentMatchers. Any suggestions would be greatly appreciated.
Your someFun method is from interface, so we can say it is open too. Thats why in mock it's overriding with returning null. To make it call basic code write following:
`when`(testObject!!.someFun("string1")).thenCallRealMethod()
By the way. Why your testObject is nullable? Set it lateinit var testObject: concreteClass to initialize it in setUp function.
What's the best way to get an instance of a generic type in Kotlin? I am hoping to find the best approximation of the following C# code:
public T GetValue<T>() where T : new() {
return new T();
}
EDIT: As mentioned in comments, this is probably a bad idea. Accepting a () -> T is probably the most reasonable way of achieving this. That said, the following technique will achieve what you're looking for, if not necessarily in the most idiomatic way.
Unfortunately, you can't achieve that directly: Kotlin is hamstrung by its Java ancestry, so generics are erased at run time, meaning T is no longer available to use directly. Using reflection and inline functions, you can work around this, though:
/* We have no way to guarantee that an empty constructor exists, so must return T? instead of T */
inline fun <reified T : Any> getValue(): T? {
val primaryConstructor = T::class.constructors.find { it.parameters.isEmpty() }
return primaryConstructor?.call()
}
If we add some sample classes, you can see that this will return an instance when an empty constructor exists, or null otherwise:
class Foo() {}
class Bar(val label: String) { constructor() : this("bar")}
class Baz(val label: String)
fun main(args: Array<String>) {
System.out.println("Foo: ${getValue<Foo>()}") // Foo#...
// No need to specify the type when it can be inferred
val foo : Foo? = getValue()
System.out.println("Foo: ${foo}") // Foo#...
System.out.println("Bar: ${getValue<Bar>()}") // Prints Bar#...
System.out.println("Baz: ${getValue<Baz>()}") // null
}