I'm writing a test case which I need some private properties. Since those private data are generated from a private method, I decided to use reflection to retrieve them after calculation is done. Later I remembered the delegated properties and decide to write a general delegate. Here is the code I got so far:
fun <T> reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty<T>(initOnce, instance)
class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> {
var initialized = false
lateinit var cache: T // <--- (a)
override opertaor fun getValue(thisRef: Any, property: KProperty<*>): Any? {
#Suppress("UNCHECKED_CAST")
if (!initialized || !initOnce) {
cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T
initialized = true
}
return cache
}
}
As you can see, property cache is initialized by getValue calls, and, if initOnce is set, subsequent calls will use that cache instead of keeping calling expensive reflection.
The very unfortunate thing is that at (a) compiler complains because T may be a nullable type and late init mechanism will be broken, but if I initialize it with null and it still complains because T may be a non-null type and null-safety is broken.
Currently, I made this to work by initializing it with the return value of a null-returning Java function. I inspected the bytecode generated and found kotlin compiler placed no null checks for that so it will work for now, but I'm worried that a future kotlin version will have such checks and ruin this trick. How I'm supposed to overcome this?
Now I'm using this, below code is released to public domain. Mention this page if you like, or do nothing.
KTHacks.java
public final class KTHacks {
private KTHacks() {
throw new UnsupportedOperationException();
}
/**
* Forge a null into a platform type and take advantage of relaxed null-checks.
* #param <T>
* #return
*/
public static <T> T NULL() {
return null;
}
}
ReflectBackedProperty.kt
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
fun <T> reflect(instance: Any, initOnce: Boolean = true) = ReflectBackedProperty<T>(initOnce, instance)
class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> {
var initialized = false
var cache: T = KTHacks.NULL()
override operator fun getValue(thisRef: Any, property: KProperty<*>): T {
#Suppress("UNCHECKED_CAST")
if (!initialized || !initOnce) {
cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T
initialized = true
}
return cache
}
}
One way is to limit T to only non-nullable types with an upper bound:
class ReflectBackedProperty<T: Any> : ReadOnlyProperty<Any, T> {}
Another approach is not to bother with lateinit at all:
class ReflectBackedProperty<T>(val initOnce: Boolean, val instance: Any): ReadOnlyProperty<Any, T> {
var initialized = false
var cache: T? = null
override operator fun getValue(thisRef: Any, property: KProperty<*>): T {
if (!initialized || !initOnce) {
cache = instance.javaClass.getDeclaredField(property.name).get(instance) as T
initialized = true
}
return cache as T
}
}
Related
Background
I'm developing a Spring Boot application and I'm using Kotlin, IntelliJ and Gradle (Groovy). I have some enum class in my code and I need to persist them (with JPA). I used a simple global converter.
// Sample Enum
enum class Policy {
PUBLIC,
INVITE_ONLY
}
// Sample Converter
#Converter(autoApply = true)
class PolicyConverter : AttributeConverter<Policy, String> {
override fun convertToDatabaseColumn(attribute: Policy): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): Policy {
return Policy.valueOf(dbData.toUpperCase())
}
}
Problem
Since I have 5-6 enums and I hate duplicated code, I thought about a generic converter that should do the work for every given enum. I tried to code something, but nothing worked. This is what I was thinking about:
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
override fun convertToDatabaseColumn(attribute: E): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): E {
return E.valueOf(dbData.toUpperCase())
}
}
In this way I can only extend from one abstract class every enum converter, like so:
#Converter(autoApply = true)
class PolicyConverter : EnumConverter<Policy>() {}
Problem with this code is that I have two errors:
E is red because: Type parameter 'E' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
valueOf is red because: unresolved reference (there are like 150+ types of .valueOf).
As suggested from this I tried to use following function:
private inline fun <reified E : Enum<E>> getValue(string: String): E {
return enumValueOf(string.toUpperCase())
}
But when called from the .convertToEntityAttribute, the result is that "Cannot use 'E' as reified type parameter. Use a class instead."
Question
So the question is simple: how can I implement an easy and fast way to make one converter for all my enums, that all follows the same principle? I just need a return E.valueOf(<value>) function, but it's not present.
A simply workaround of this problem is to define an abstract method that every class will implement and it will return the correct type, given a string.
// Inside EnumConverter, the Generic Class
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
abstract fun getValueFromString(string: String) : E
override fun convertToEntityAttribute(dbData: String): E {
return getValueFromString(dbData)
}
[...]
}
// Inside Policy Enum, implementing
class PolicyConverter : EnumConverter<Policy>() {
override fun getValueFromString(string: String): Policy {
return Policy.valueOf(string.toUpperCase())
}
}
But it's a workaround that I really dislike.
This is giving an error:
class Apple(weightInGrams: Float){
fun grow() {
weightInGrams+= 2.0f
}
}
First of all, the equivalent of void (in Java) is Unit (in Kotlin), and the type a function returns goes at the end, so you should use fun grow(): Unit { ... } instead of fun void grow() { ... }. Moreover, you can omit Unit and just write fun grow() { ... } because the compiler knows that your function doesn't return any meaningful value.
Now, I'll try to explain the basics to answer your question and give you some clarity. In Java, the parameters of a constructor are visible only inside that constructor. In Kotlin, the parameters are only visible in initializer blocks and in property initializers, unless you transform them into properties. Let's explain all this with examples.
In Java, we see constructors in classes like this many times:
public class Person {
public final String name;
public final Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
The parameters are used to initialize the fields of the class Person.
In Kotlin, the equivalent could be:
a) Use the parameters in initializer blocks.
class Person(name: String, age: Int) {
val name: String
val age: Int
init {
this.name = name
this.age = age
}
}
b) Use the parameters in property initializers declared in the class body.
class Person(name: String, age: Int) {
val name = name
val age = age
}
c) Declaring properties and initializing them directly in the primary constructor.
class Person(val name: String, val age: Int)
Therefore, if you write var or val, the parameters of the constructor will be also properties and you will be able to use them in your class like you want to do inside your function grow.
So, your final code should be:
class Apple(var weightInGrams: Float) {
fun grow() {
weightInGrams += 2.0f
}
}
var because you are assigning a value to weightInGrams multiple times.
make your property a class member
class Apple(var weightInGrams: Float){
fun void grow() {
weightInGrams+= 2.0f
}
}
I understand the question was already answered.
If you want to initialize an apple with an initialWeight, you can do it as below. The init block can help initialize the value and the grow function can effectively work on the actual variable without a need to declare the constructor variable as var:
class Apple(initWeight: Float){
var weightInGrams = 0.0f
init {
var weightInGrams = initWeight
}
fun grow() {
weightInGrams+= 2.0f
}
}
fun main(args: Array<String>) {
val a = Apple(10.0f)
a.grow()
println(a.weightInGrams)
};
Assuming that we have an object with the following attributes:
public class MyObject {
private String attr1;
private Integer attr2;
//...
public String getAttr1() {
return this.attr1;
}
public Integer getAttr2() {
return this.attr2;
}
}
One way of sorting a list mylist of this object, based on its attribute attr1 is:
mylist.sort(Comparator.comparing(MyObject::getAttr1));
Is it possible to use this code inside a method in a dynamic way and replace the getAttr1 part with a method that returns the getter of an attribute of the object based on its name? Something like:
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(Comparator.comparing(MyObject::getGetterByAttr(attr)));
}
The MyObject::getGetterByAttr(attr) part does not compile, I wrote it just as an example to explain my idea
I tried to implement a method with the following code new PropertyDescriptor(attr, MyObject.class).getReadMethod().invoke(new MyObject()) but It's still not possible to call a method with a parameter from the comparing method
You could add a method like
public static Function<MyObject,Object> getGetterByAttr(String s) {
switch(s) {
case "attr1": return MyObject::getAttr1;
case "attr2": return MyObject::getAttr2;
}
throw new IllegalArgumentException(s);
}
to your class, but the returned function is not suitable for Comparator.comparing, as it expects a type fulfilling U extends Comparable<? super U> and while each of String and Integer is capable of fulfilling this constraint in an individual invocation, there is no way to declare a generic return type for getGetterByAttr to allow both type and be still compatible with the declaration of comparing.
An alternative would be a factory for complete Comparators.
public static Comparator<MyObject> getComparator(String s) {
switch(s) {
case "attr1": return Comparator.comparing(MyObject::getAttr1);
case "attr2": return Comparator.comparing(MyObject::getAttr2);
}
throw new IllegalArgumentException(s);
}
to be used like
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(getComparator(attr));
}
This has the advantage that it also may support properties whose type is not Comparable and requires a custom Comparator. Also, more efficient comparators for primitive types (e.g. using comparingInt) would be possible.
You may also consider using a Map instead of switch:
private static Map<String,Comparator<MyObject>> COMPARATORS;
static {
Map<String,Comparator<MyObject>> comparators=new HashMap<>();
comparators.put("attr1", Comparator.comparing(MyObject::getAttr1));
comparators.put("attr2", Comparator.comparing(MyObject::getAttr2));
COMPARATORS = Collections.unmodifiableMap(comparators);
}
public static Comparator<MyObject> getComparator(String s) {
Comparator<MyObject> comparator = COMPARATORS.get(s);
if(comparator != null) return comparator;
throw new IllegalArgumentException(s);
}
More dynamic is only possible via Reflection, but this would complicate the code, add a lot of potential error source, with only little benefit, considering that you need only to add one line of source code for adding support for another property in either of the examples above. After all, the set of defined properties gets fixed at compile time.
You could also have a single place where this comparators would be defined:
static enum MyObjectComparator {
ATTR1("attr1", Comparator.comparing(MyObject::getAttr1));
MyObjectComparator(String attrName, Comparator<MyObject> comparator) {
this.comparator = comparator;
this.attrName = attrName;
}
private final Comparator<MyObject> comparator;
private final String attrName;
private static MyObjectComparator[] allValues = MyObjectComparator.values();
public static Comparator<MyObject> findByValue(String value) {
return Arrays.stream(allValues)
.filter(x -> x.attrName.equalsIgnoreCase(value))
.map(x -> x.comparator)
.findAny()
.orElseThrow(RuntimeException::new);
}
}
And your usage would be:
public void sortListByAttr(List<MyObject> list, String attr) {
list.sort(MyObjectComparator.findByValue(attr));
}
I have a model TestModel.java which contains a field declared like below
#Required
#IntegerAnnotation
#Column (length = 4, nullable = false)
public Integer sort;
For the default validation annotation of Play doesn't support Integer check, so I create an annotation validation for checking the input value is an Integer or not.
The IntegerAnnotation.java :
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Constraint(checkWith = IntegerCheck.class)
public #interface IntegerAnnotation {
String message() default IntegerCheck.message;}
}
and this annotation refers to an implementations of IntegerCheck.java
public class IntegerCheck extends AbstractAnnotationCheck<IntegerAnnotation>{
final static String message = "validation.integer";
#Override
public boolean isSatisfied(Object validatedObject, Object value, OValContext context, Validator validator) throws OValException {
// TODO Auto-generated method stub
if(value == null) return false;
return isInteger(value.toString());
}
private static boolean isInteger(String s) {
return isInteger(s,10);
}
private static boolean isInteger(String s, int radix) {
if(s.isEmpty()) return false;
for(int i = 0; i < s.length(); i++) {
if(i == 0 && s.charAt(i) == '-') {
if(s.length() == 1) return false;
else continue;
}
if(Character.digit(s.charAt(i), radix) < 0) return false;
}
return true;
}
}
And there are two actions in the controller for create or update the TestModel, method create and method update.
public static void create(#Valid TestModel testModel){
if(validation.hasErrors()) {
params.flash();
validation.keep();
blank();
}
}
public static void update(#Valid TestModel testModel) {
if(validation.hasErrors()) {
params.flash();
validation.keep();
}
else{
testModel.save();
show(sys5000.id);
}
}
I found when the field sort is not entered with integer value, the value will be null in the create method, that's why I put a if null condition in the IntegerCheck.class.
Thus, if the value of filed sort is wrong typed, it detects null and return false.
Though it's not what I expect it will use the wrong typed value to verify, it worked...sort of.
The problem is in the update method.
For when updating an instance of TestModel, it won't show the wrong typed value, but the original retrieved value from database instead.
It's really a problem, for it will always return true if the retrieved data from database is already an integer. Then the validation won't do the work.
And the questions are:
Any advice for this validation strategy? I think maybe I'm apart from the right way to verify the Integer value validation.
How can I retrieve the wrong typed value from the action, or it's just not possible, for it's already not a valid data type of that field.
The behavior that you see in the update method is the way Play works!
Here is the relevant section from the documentation :
... When Play finds the id field, it loads the matching instance from
the database before editing it. The other parameters provided by the
HTTP request are then applied.
So in your case, when the sort property is null during an update, the value in the database is used.
Now if I were to try to achieve what you are trying to do, I would propably do it this way :
In my model
#Required
#CheckWith(IntegerCheck.class)
public int sort; // using primitive here to avoid null values.
static class IntegerCheck extends Check {
public boolean isSatisfied(Object model, Object sort) {
int fieldValue = (int) sort; // here you have the value as int
// Here you would check whatever you want and return true or false.
return ...
}
}
I have this object
#Validateable
class Foo {
Map<String, String> items
static constraints = {
items minSize: 1
}
}
but this test fail:
#Test
void shouldNotValidateIfItemsIsEmpty() {
Foo foo = new Foo(items: [:])
assert !foo.validate()
}
What do I do wrong? It's supposed to work according to grails 'minSize' documentation: "Sets the minimum size of a collection or number property."
The documentation might be misleading. The minSize constraint will only apply to:
String
Arrays
Classes which implements the java.util.Collection interface
java.util.Map however does not extend the java.util.Collection interface
See the supports method of MinSizeConstraint:
public boolean supports(Class type) {
return type != null && (
String.class.isAssignableFrom(type) ||
Collection.class.isAssignableFrom(type) ||
type.isArray());
}
You can develop your own custom constraint for this or a custom validator as suggested by Thermech
In addition, in order for Grails to mock the validate method properly your test class should be something like:
#TestMixin(ControllerUnitTestMixin) class FooTest {
#Test
void shouldNotValidateIfItemsIsEmpty() {
Foo foo = mockCommandObject Foo
foo.items = [:]
assert !foo.validate()
} }
The only way I found, is with a custom validator:
static constraints = {
items validator: { Map map, obj, errors ->
if (map.size() < 1) errors.rejectValue('items', 'minSize.notmet')
}
}