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

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;
}

Related

How can I get generic type of PsiClassReferenceType

public class RestControllerExample {
#GetMapping("/test")
public Test<String, Integer> req() {
return null;
}
static class Test<T, V> {
private T fieldT;
private V fieldV;
public T getFieldT() {
return fieldT;
}
public void setFieldT(T fieldT) {
this.fieldT = fieldT;
}
public V getFieldV() {
return fieldV;
}
public void setFieldV(V fieldV) {
this.fieldV = fieldV;
}
}
}
PsiClassReferenceType is Test<String, Integer>. How can I know fieldT is String and fieldV is Integer?
class Test<T, V> just a demo, I can't use the getParameters() method to get the type because the order of the fields is not fixed. It is possible that fieldT is declared after fieldV
field.getType().substitute(classReferenceType.resolveGenerics().getSubstitutor()) should do the job.
fieldT.getType() in your example must return T
classReferenceType.resolveGenerics() (returns a pair of class and its substitutor), in your example Test and T -> String, V -> Integer
method substitute would replace T in the type with a String, V with an Integer

How to address a getter from inside the class in Kotlin

Consider following Kotlin-Code:
class Foo(input: Int) {
private var someField: Int = input
get() = -field
set(value) {
field = -value
}
fun bar() {
println(someField)
}
}
fun main() {
Foo(1).bar()
}
This prints -1 in the console which means that inside method bar() someField references the attribute and not the corresponding getter. Is there a way that allows me to use the get()-method as if I was referencing this field from outside?
Perhaps you could track the "raw" value separately from the negative value? Something like this:
class Foo(input: Int) {
private var _someField: Int = input
var someField: Int
get() = -_someField
set(value) {
_someField = -value
}
fun bar() {
println(someField)
}
}
Now the class internals can reference _someField to deal directly with the raw value, while outside clients can only "see" someField.

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}]}

Java to Kotlin conversion extending Abstract class

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
}

Why Kotlin function with default parameters creates a method with unused parameter

See this example code in Kotlin:
fun foo(bar: Int = 0, baz: Int) {
/* ... */
}
After decompiling it to Java code (Tools -> Kotlin -> Show Kotlin Bytecode -> Decompile) I got the following code
public static final void foo(int bar, int baz) {
}
// $FF: synthetic method
// $FF: bridge method
public static void foo$default(int var0, int var1, int var2, Object var3) {
if ((var2 & 1) != 0) {
var0 = 0;
}
foo(var0, var1);
}
I noticed that the resulting Java method has an unused Object var3 parameter.
I kind of thought that it may be related to functions in a class but when decompiling this code
class Foo {
fun foo(bar: Int = 0, baz: Int) {
/* ... */
}
}
I got this code
public final class Foo {
public final void foo(int bar, int baz) {
}
// $FF: synthetic method
// $FF: bridge method
public static void foo$default(Foo var0, int var1, int var2, int var3, Object var4) {
if ((var3 & 1) != 0) {
var1 = 0;
}
var0.foo(var1, var2);
}
}
As you can see the Object parameter is still unused and just sits there.
Upon additional tests I noticed the same behavior for extension methods. The same goes when the default parameter is last (i.e. fun foo(bar: Int, baz: Int = 0) {})
I've also done a basic test to check what is that value set to when calling that function using the code below
fun main(args: Array<String>) {
foo(baz = 2)
}
And
class Something {
init {
foo(baz = 2)
}
}
After decompiling it I got the following code
public static final void main(#NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
foo$default(0, 2, 1, (Object)null);
}
And
public final class Something {
public Something() {
FooKt.foo$default(0, 2, 1, (Object)null);
}
}
Which makes even less sense whatsoever.
My question is: Why does Kotlin generate an unused parameter for functions with default parameters? Is it a bug?
According to this, currently it's unused, but is reserved for adding super calls with defaults later.
You can see it in action here:
open class Foo {
open fun foo(bar: Int = 0, baz: Int) {
/* ... */
}
}
class Blah: Foo() {
override fun foo(bar: Int, baz: Int) {
}
}
which will generate a bytecode-to-Java Foo of:
public class Foo {
public void foo(int bar, int baz) {
}
// $FF: synthetic method
// $FF: bridge method
public static void foo$default(Foo var0, int var1, int var2, int var3, Object var4) {
if(var4 != null) {
throw new UnsupportedOperationException("Super calls with default arguments not supported in this target, function: foo");
} else {
if((var3 & 1) != 0) {
var1 = 0;
}
var0.foo(var1, var2);
}
}
}