Java to Kotlin conversion extending Abstract class - kotlin

I'm converting a Java class that extends an abstract class as per the code below
public class BadRequestAlertException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
private final String entityName;
private final String errorKey;
public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
}
public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey));
this.entityName = entityName;
this.errorKey = errorKey;
}
public String getEntityName() {
return entityName;
}
public String getErrorKey() {
return errorKey;
}
private static Map<String, Object> getAlertParameters(String entityName, String errorKey) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("message", "error." + errorKey);
parameters.put("params", entityName);
return parameters;
}
}
Into Kotlin as per the implementation below
class BadRequestAlertException(type: URI = ErrorConstants.DEFAULT_TYPE, defaultMessage: String, val entityName: String, val errorKey: String) : AbstractThrowableProblem(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey)) {
companion object {
private const val serialVersionUID = 1L
private fun getAlertParameters(entityName: String, errorKey: String): Map<String, Any> {
val parameters = HashMap<String, Any>()
parameters["message"] = "error.$errorKey"
parameters["params"] = entityName
return parameters
}
}
}
The Kotlin implementation raises the following error Class 'BadRequestAlertException' is not abstract and does not implement abstract base class member public abstract fun getCause(): Exceptional! defined in org.zalando.problem.AbstractThrowableProblem.
Why doesn't the Java version implement the getClause() method, but Kotlin requires the method to be overridden? Is there a problem with the conversion, or just the way Kotlin extends abstract classes?

FWIW it appears as though the legitimate bug that originally caused this issue is fixed in Kotlin 1.5.0. See KT-45853 ("JVM / IR: "Accidental override" caused by inheriting Throwable.getCause from Java interface") for details... the following Kotlin class compiles & runs as far as I can tell:
import com.fasterxml.jackson.annotation.JsonIgnore
import java.net.URI
import org.zalando.problem.AbstractThrowableProblem
import org.zalando.problem.Exceptional
import org.zalando.problem.Status
class BadRequestAlertExceptionKt(
type: URI = URI.create("about:blank"),
defaultMessage: String,
val entityName: String,
val errorKey: String
) : AbstractThrowableProblem(
type,
defaultMessage,
Status.BAD_REQUEST,
null,
null,
null,
getAlertParameters(entityName, errorKey)
) {
companion object {
private const val serialVersionUID = 1L
private fun getAlertParameters(entityName: String, errorKey: String): Map<String, Any> {
val parameters = HashMap<String, Any>()
parameters["message"] = "error.$errorKey"
parameters["params"] = entityName
return parameters
}
}
#JsonIgnore
override fun getCause(): Exceptional? = super.cause
}

Related

Kotlin: IllegalAccessException: Class BlockJUnit4ClassRunner can not access a member of class Foo with modifiers “private”

Using Kotlin with Junit 4 I get the following exception for Parameter field injection:
java.lang.IllegalAccessException: Class org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters can not access a member of class MyTestClass with modifiers "private"
Here's the code:
#RunWith(Parameterized::class)
class MyTestClass {
#Rule
#JvmField
val mockitoRule: MockitoRule = MockitoJUnit.rule()
companion object {
#Parameters(name = "{0}")
#JvmStatic
fun testData() = listOf(
arrayOf(1, 1),
arrayOf(2, 2),
arrayOf(3, 3)
)
}
#Parameter
var input: Int = 0 // Public
#Parameter(1)
var expected: Int = 0 // Public
#Test
fun foo() {
assertEquals(expected, input)
}
}
Any ideas?
Tl;dr: Adding #JvmField to both fields solved the problem.
Like so:
#JvmField
#Parameter
var input: Int = 0
#JvmField
#Parameter(1)
var expected: Int = 0
Explanation: By default, Kotlin will make the fields private and generate getters/setters as can be seen from the decompiled java code below, as a result JUnit won't be able to read the private fields hence the message: can not access a member of class MyTestClass with modifiers "private"
#Parameter
private int input;
#Parameter(1)
private int expected;
public final int getInput() {
return this.input;
}
public final void setInput(int var1) {
this.input = var1;
}
public final int getExpected() {
return this.expected;
}
public final void setExpected(int var1) {
this.expected = var1;
}

Derived class properties not getting serialized in kotlin

I am trying to serialize derived object using Gson in Kotlin and on serialization I am getting only parent class attributes Gson is ignoring all derived class attributes.
Here is my code:
fun main(args: Array<String>) {
val somename = Sample(1, "somename")
val car = Car(somename, 4)
val car2 = Car(somename, 4)
val vehicles = object : ArrayList<Vehicle>() {
}
vehicles.add(car)
vehicles.add(car2)
val sampleClassResponse = SampleClassResponse(1, vehicles)
val gson = Gson()
val x = gson.toJson(sampleClassResponse)
println(x)
// System.out.println(car.tyre);
}
// Base class:
public class Vehicle {
int number;
String name;
}
Sub class:
import Sample.Sample;
public class Car extends Vehicle {
int tyre;
public Car(Vehicle sample, int tyre) {
super(sample.number, sample.name);
this.tyre = tyre;
}
}
SampleResponse class
public class SampleClassResponse {
private int status;
private List<Vehicle> vehicles;
public SampleClassResponse(int status, List<Vehicle> vehicles){
this.status = status;
this.vehicles = vehicles;
}
}
So here in the main class when I am serializing SampleClass response I am getting output as(with ignored Tyre values)
{"vehicles":[{"number":1,"name":"somename"},{"number":1,"name":"somename"}],"status":1}
and when same code is written in java I am getting output as:
{"status":1,"vehicles":[{"tyre":4,"number":1,"name":"somename"},{"tyre":4,"number":1,"name":"somename"}]}
Could anyone help me in figuring out what am i doing wrong in this kotlin code.
Note: I have used intellij converter to convert code to Kotlin
Here is the java code
public class MainObject {
public static void main(String[] args) {
Sample somename = new Sample(1, "somename");
Car car = new Car(somename, 4);
Car car2 = new Car(somename, 4);
ArrayList vehicles = new ArrayList<Vehicle>() {
};
vehicles.add(car);
vehicles.add(car2);
SampleClassResponse sampleClassResponse = new
SampleClassResponse(1, vehicles);
Gson gson = new Gson();
String x = gson.toJson(sampleClassResponse);
System.out.println(x);
}
}
Base Class:
public class Vehicle {
int number;
String name;
}
Derived class:
public class Car extends Vehicle {
int tyre;
public Car(Sample sample, int tyre) {
this.number = sample.number;
this.name = sample.name;
this.tyre = tyre;
}
}
SampleResponse class:
public class SampleClassResponse {
int status;
List<Vehicle> vehicles = new ArrayList<>();
public SampleClassResponse(int status, List<Vehicle> vehicles){
this.status = status;
this.vehicles = vehicles;
}
}
your code does not compile and that's why I do not know where the problem is.
my proposition of the code in the kotlin is as follows
fun main(args: Array<String>) {
val vehicle = Vehicle("somename", 1)
val car = Car(vehicle, 4)
val car2 = Car(vehicle, 4)
val vehicles = listOf(car, car2)
val response = SampleClassResponse(1, vehicles)
val json = Gson().toJson(response)
println(json)
}
class SampleClassResponse(val status: Int, val vehicles: List<Vehicle>)
open class Vehicle(var name: String?, var number: Int = 0)
class Car(sample: Vehicle, var tyre: Int) : Vehicle(sample.name, sample.number)
now is what you would like to get
{"status":1,"vehicles":[{"tyre":4,"name":"somename","number":1},{"tyre":4,"name":"somename","number":1}]}

Using #AutoAnnotation in Kotlin, complaint method must be static

In reference to https://google.github.io/dagger/multibindings.html, there's this code
#AutoAnnotation
static MyKey createMyKey(String name, Class<?> implementingClass, int[] thresholds) {
return new AutoAnnotation_MyComponentTest_createMyKey(name, implementingClass, thresholds);
}
When converted to Kotlin, I use below
companion object {
#AutoAnnotation
#JvmStatic
fun createMyKey(name: String, implementingClass: Class<*>, thresholds: IntArray): MyKey {
return AutoAnnotation_MainActivity_createMyKey(name, implementingClass, thresholds)
}
}
It still complaints
error: #AutoAnnotation method must be static
public final com.elyeproj.daggermultibinding.MyKey createMyKey(#org.jetbrains.annotations.NotNull()
^
I tried both with and without #JvmStatic still not working. How to resolve this?
The following works for me. Have the create key method as global function.
class HelloAutoAnnotations {
fun execute() {
println("HelloAutoAnnotations...")
DaggerMyComponent.create().myMap().forEach(::println)
}
}
#MapKey(unwrapValue = false)
private annotation class MyKey(val username: String, val password: String)
#Module
private class MyModule {
#Provides
#IntoMap
#MyKey(username = "user1", password = "T0gether")
fun providesUser(): String = "Rooney"
#Provides
#IntoMap
#MyKey(username = "user2", password = "T0gether")
fun provideUser(): String = "Doo"
}
#Component(modules = arrayOf(MyModule::class))
private interface MyComponent {
fun myMap(): Map<MyKey, String>
}
#AutoAnnotation
private fun createMyKey(username: String, password: String): MyKey {
return MyKeyCreator.createMyKey(username, password)
}
According to the docs:
Maps whose keys are not known at compile time
Map multibindings work only if your map’s keys are known at compile
time and can be expressed in an annotation. If your map’s keys don’t
fit in those constraints, then you cannot create a multibound map, but
you can work around that by using set multibindings to bind a set of
objects that you can then transform into a non-multibound map.
#Module
class MyModule {
#Provides #IntoSet
static Map.Entry<Foo, Bar> entryOne(...) {
Foo key = ...;
Bar value = ...;
return new SimpleImmutableEntry(key, value);
}
#Provides #IntoSet
static Map.Entry<Foo, Bar> entryTwo(...) {
Foo key = ...;
Bar value = ...;
return new SimpleImmutableEntry(key, value);
}
}
#Module
class MyMapModule {
#Provides
static Map<Foo, Bar> fooBarMap(Set<Map.Entry<Foo, Bar>> entries) {
Map<Foo, Bar> fooBarMap = new LinkedHashMap<>(entries.size());
for (Map.Entry<Foo, Bar> entry : entries) {
fooBarMap.put(entry.getKey(), entry.getValue());
}
return fooBarMap;
}
}
So you should try this approach perhaps.

Kotlin - what's the reasoning behind "const"

It has been already clarified what's the difference between val and const val here.
But my question is, why we should use const keyword? There is no difference from the generated Java code perspective.
This Kotlin code:
class Application
private val testVal = "example"
private const val testConst = "another example"
Generates:
public final class ApplicationKt
{
private static final String testVal = "example";
private static final String testConst = "another example";
}
It's not always the same generated code.
If testVal and testConst were public, the generated code wouldn't be the same. testVal would be private with a public get, whereas testConst would be public, without any getter. So const avoids generating a getter.
In my opinion the main difference is that val means that no setter will be generated for the property (but a getter will be generated) and not that the value is constant, while a const val is a constant (like a Java's private/public static final xxx).
Example:
class Foo {
private val testVal: String
get() = Random().nextInt().toString()
}
As directly mentioned in the documentation, testConst can be used in annotation parameters, but testVal can't.
More generally speaking, const guarantees that you have a constant variable in the Java sense, and
Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1), reachability (§14.21), and definite assignment (§16.1.1).
You don't see the difference between generated code because your variables are private. Otherwise the result would have the getter for testVal:
public final class ApplicationKt {
#NotNull
private static final String testVal = "example";
#NotNull
public static final String testConst = "another example";
#NotNull
public static final String getTestVal() {
return testVal;
}
}
So in your particular case it is the same, except you can use const properties in annotations:
const val testVal: String = "This subsystem is deprecated"
#Deprecated(testVal) fun foo() { ... }
There are also differences in using them.
Example of constants(Kotlin):
class Constants {
companion object {
val EXAMPLE1 = "example1" // need companion and a getter
const val EXAMPLE2 = "example2" // no getter, but companion is generated and useless
#JvmField val EXAMPLE3 = "example3"; // public static final with no getters and companion
}
}
How to use(Java):
public class UseConstants {
public void method(){
String ex1 = Constants.Companion.getEXAMPLE1();
String ex2 = Constants.EXAMPLE2;
String ex3 = Constants.EXAMPLE3;
}
}
"Consts" are compile time Constants whereas "val" is used to define constants at run time.
This means, that "consts" can never be assigned to a function or any class constructor, but only to a String or primitive.
Example:
const val NAME = "M Sakamoto"
val PICon = getPI()
fun getPI(): Double {
return 3.14
}
fun main(args: Array<String>) {
println("Name : $NAME")
println("Value of PI : $PICon")
}
Output:
Name : M Sakamoto
Value of PI : 3.14

Locking down the serialVersionUID in Kotlin

I've been battling the whole morning to lock down the serialVersionUID in a Kotlin class. I have a BaseModel which is extended by Project
abstract class BaseModel<T>(
var id: Int? = null,
private val fileName: String,
private val data: MutableList<T>,
private val indices: MutableMap<Int, T>
) : Serializable {
...
protected fun writeToDisk() {
val oos = ObjectOutputStream(BufferedOutputStream(FileOutputStream(fetchFileName())) )
oos.writeObject(fetchData());
oos.close();
}
}
And the project class:
class Project(
var name: String = "",
var repo: String = ""
) : BaseModel<Project>(
data = Data.projects,
indices = Data.projectsIndex,
fileName = "data/projects.dat"
), Serializable {
...
override fun toString(): String {
return "Project: id=${id}, name=${name}, repo=${repo}"
}
}
Every time I write to Disk and then change anything in the class and try to read it back again, I would get:
java.io.InvalidClassException: com.jvaas.bob.model.Project; local
class incompatible: stream classdesc serialVersionUID =
4156405178259085766, local class serialVersionUID =
2024101567466310467
I've tried adding:
private val serialVersionUID: Long = 1
to all classes with no effect.
Some examples on StackOverflow were using serialVersionUid which had no effect either (I believe this is intelliJ lowercasing the last two letters for some reason)
#JvmStatic doesn't work here since it's not an object, I've tried making it non-private with no success.
You can define serialVersionUID as a constant in a companion object:
abstract class BaseModel<T> : Serializable {
companion object {
private const val serialVersionUID: Long = -1
}
}
Constants are compiled to fields, and fields of a companion are stored as static fields of the class that contains companion. Therefore you get what you need – a private static field serialVersionUID in your serializable class.
The solution was actually much simpler than I thought, use a companion object. This now serializes perfectly and if I add more fields, it still serializes to disk and deserializes unless I change the serialVersionUID
Base:
abstract class BaseModel<T>(
var id: Int? = null,
private val fileName: String,
private val data: MutableList<T>,
private val indices: MutableMap<Int, T>
) : Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 1
}
...
}
Project:
class Project(
var name: String = "",
var repo: String = ""
) : BaseModel<Project>(
data = Data.projects,
indices = Data.projectsIndex,
fileName = "data/projects.dat"
), Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 1
}
override fun toString(): String {
return "Project: id=${id}, name=${name}, repo=${repo}"
}
}
Install this plugin: GenerateSerialVersionUID,use plugin to auto generate default serial version uid,usage: Click here.