I have a custom Exception class which looks like this:
class GenericException(message: String?, errorCode: Int) : RuntimeException(message), GraphQLError {
.....
}
As you all know, RuntimeException extends Throwable which has a method called getMessage()
Now the issue is, this interface GraphQLError (which is a library interface) also has a method called getMessage()
As a result, compiler is complaining with this:
OK so I implement the method:
override fun getMessage(): String {
TODO("Not yet implemented")
}
Now I get this:
What am I supposed to do here?
What I guessed in the comments was right, kotlin allows multiple inheritence. It was indeed because of the Throwable class.
You can use #JvmField annotation to instruct the compiler not to generate getters and setters for the field and then create the getter/setter yourself.
interface HasMessage {
fun getMessage(): String
}
class GenericException(
#JvmField override val message: String?, // var is also possible
val errorCode: Int // I made it a property, might not be as well
) : RuntimeException(message), HasMessage {
override fun getMessage(): String {
// return of the super's getter, probably no use because you have field as property in this class
val superGetMessage = super<RuntimeException>.message
TODO("Not yet implemented")
}
}
Play with the code yourself.
Related
I have a base class:
abstract class JSONDeserializationStrategy<T : Any>: DeserializationStrategy<T> {
protected abstract fun parse(json: JsonObject): T
protected abstract fun getSerializationException(): SerializationException
}
and then a derived class
class MyClassParserDeserializationStrategy : JSONDeserializationStrategy<MyClass>() {
override val descriptor: SerialDescriptor
= buildClassSerialDescriptor("MyClass")
override fun getSerializationException(): SerializationException
= throw SerializationException("Invalid JSON received for MyClass.")
How could I move the property descriptor and the method getSerializationException from the derived class into the base class, since they only "adapt" by providing their name as String? I was trying to do something in the direction of T::class.java.simpleName as String but it didnt work. What is the best way to do this?
As #Tenfour04 explained, T is erased, so it is not directly accessible. However, as long as the subclass of JSONDeserializationStrategy provides the T as a specific class/type, it can be acquired with a bit of reflection voodoo:
fun main() {
val strategy = MyClassParserDeserializationStrategy()
println(strategy.descriptor.serialName) // MyClass
}
abstract class JSONDeserializationStrategy<T : Any>: DeserializationStrategy<T> {
protected val type: KType = this::class.supertypes
.first { it.classifier == JSONDeserializationStrategy::class }
.arguments[0].type!!
#Suppress("UNCHECKED_CAST")
protected val typeClass = requireNotNull(type.classifier as? KClass<T>) {
"T is unknown"
}
override val descriptor: SerialDescriptor = buildClassSerialDescriptor(typeClass.simpleName!!)
fun getSerializationException(): SerializationException =
throw SerializationException("Invalid JSON received for ${typeClass.simpleName!!}.")
...
}
I'm not 100% sure, but I believe both type!! and simpleName!! are safe, they can't be null.
Also, it won't work if T is really fully erased and unknown, e.g.:
val strategy = GenericParserDeserializationStrategy<MyClass>() // exception
For this reason it makes sense to open type/typeClass properties for overriding, so generic non-abstract subclasses could provide their own means to acquire T. However, then we would probably need to move the initialization of most of properties outside of the constructor.
Because of type erasure, T's class is not accessible. Work-around could be to add it as a constructor property that returns the type, and then the subclasses must pass the type. The property needs to be in the constructor, rather than provided as an abstract property for subclasses to override because you need it to initialize descriptor at instantiation time. (It's highly discouraged to call an open property at class initialization time.)
abstract class JSONDeserializationStrategy<T : Any>(protected val typeClass: KClass<out T>): DeserializationStrategy<T> {
protected abstract fun parse(json: JsonObject): T
override val descriptor: SerialDescriptor = buildClassSerialDescriptor(typeClass.simpleName!!)
fun getSerializationException(): SerializationException =
throw SerializationException("Invalid JSON received for ${typeClass.simpleName}.")
}
class TodaySaleParserDeserializationStrategy : JSONDeserializationStrategy<TodaySale>(TodaySale::class) {
}
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")
}
}
I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}
I am trying to create a POJO (aka data classes in Kotlin) structure of a JSON response in Kotlin. I've implemented the Parcelable interface for each data class in the structure. In all of the data classes, I've auto generated the Parcelable implementation. The issue is the generated second constructor where the IDE is complaining about:
Overload resolution ambiguity
It states that it's being confused between these two constructors:
public constructor GeocodeRes(parcel: Parcel)
public constructor GeocodeRes(responset: ResponseRes)
Which I believe makes sense because ResponseRes is also of type Parcelable (ResponseRes implements Parcelable). So calling the GeocodeRes(parcel) method (within the createFromParcel companion method), it is getting confused.
That was until I removed ResponseRes from implementing the Parcelable class and it's still showing the same error.
Is there any reason to this? Am I setting this up properly? In all of the children data classes, they all implement the Parcelable interface (with dependence with eachother) but aren't running into any issues.
Here's my GeocodeRes class:
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
data class GeocodeRes(
#SerializedName("Response") #Expose val responset: ResponseRes
) : Parcelable {
// this is the problem. the IDE is complaining that the usage is too ambiguous (). however, the only usage of this constructor is within this class - just doesn't tell me where exactly.
constructor(parcel: Parcel) : this(parcel.readParcelable(ResponseRes::class.java.classLoader)) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(responset, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GeocodeRes> {
override fun createFromParcel(parcel: Parcel): GeocodeRes {
return GeocodeRes(parcel)
}
override fun newArray(size: Int): Array<GeocodeRes?> {
return arrayOfNulls(size)
}
}
}
Here's my ResponseRes class:
data class ResponseRes(
#SerializedName("MetaInfo") #Expose val metaInfo: MetaInfo,
#SerializedName("View") #Expose val views: List<View>
): Parcelable
{
[...]//parcel methods
}
however, the only usage of this constructor is within this class - just doesn't tell me where exactly
The problem is with the definition itself, not with any usage. It could never be used, and the error would still be there.
You should be able to fix this by specifying which Parcelable you want to read:
this(parcel.readParcelable<ResponseRes>(ResponseRes::class.java.classLoader))
The compiler can't decide if you mean that or
this(parcel.readParcelable<Parcel>(ResponseRes::class.java.classLoader))
Even though the second wouldn't be legal because Parcel doesn't implement Parcelable, if you look at the signature
<T extends Parcelable> T readParcelable(ClassLoader loader)
you can see only the return type can be used to infer T, not the argument. So the compiler need to pick the constructor overload before trying to infer T.
This is the code snippet:
abstract class SuperClass {
init {
toOverride()
}
abstract fun toOverride()
}
class ChildClass : SuperClass() {
private val innerClass = InnerClass()
override fun toOverride() {
innerClass.doSomething()
}
class InnerClass {
fun doSomething() = Unit
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val superClass = ChildClass()
superClass.toOverride()
}
}
Calling superClass.toOverride() will cause null pointer exception but from ChildClass point of view innerClass is an immutable field. The problem is that in the constructor of SuperClass toOverride() is being called.
We don't have control over how SuperClass is written in a lot of cases. In android framework there is a lot of such cases, for example http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/android/view/View.java#20160 will be called in the constructor of View.java. How do we address those issues? Adding null checks will trigger IDE warning and looks like kotlin compiler will trim the null check sometimes if you use ?. operator since it think the ?. is not necessary.
To start with, I'd read What's wrong with overridable method calls in constructors?
With that out of the way, and if you really can't control what the parent class does and you really really need it to do what it's doing, you can probably do something like this (Only showing the ChildClass:
class ChildClass : SuperClass() {
private val innerClass: InnerClass? = InnerClass()
init {
// beware: this is ONLY done because toOverride ONLY calls
// innerClass.doSomething(), you have to make sure that anything else
// there is idempotent
toOverride()
}
override fun toOverride() {
innerClass?.doSomething()
}
}
My recommendation would be to think if there's an alternative implementation to your class hierarchy.