Writing some querydsl code. In Java I would do like this:
#Test
void countTest() {
NumberPath<Long> cnt = Expressions.numberPath(Long.class, "count");
NumberPath<Long> typeId = Expressions.numberPath(Long.class, "type_id");
List<Long> fetched = sql.select(typeId)
.from(SQLExpressions.select(tGroup.typeId.as(typeId), tGroup.count().as(cnt))
.from(tGroup)
.groupBy(tGroup.typeId))
.where(cnt.gt(100L)).fetch();
System.out.println(fetched);
}
Notice this Long.class in Expressions.numberPath(Long.class, ...)
If I create a .kt file and copy-paste the above Java code, it will be converted by Intellij to:
Expressions.numberPath(Long::class.java, ...).
So the resulting Kotlin code I have is:
val cnt = Expressions.numberPath(Long::class.java, "count")
val typeId = Expressions.numberPath(Long::class.java, "type_id")
val fetched = sql.select(typeId)
.from(SQLExpressions.select(QTGroup.tGroup.typeId.`as`(typeId), QTGroup.tGroup.count().`as`(cnt))
.from(QTGroup.tGroup)
.groupBy(QTGroup.tGroup.typeId))
.where(cnt.gt(100L)).fetch()
println(fetched)
Now when I run the code, I get:
java.lang.IllegalArgumentException: Unsupported target type : long
at com.querydsl.core.util.MathUtils.cast(MathUtils.java:86)
at com.querydsl.core.types.dsl.NumberExpression.cast(NumberExpression.java:178)
at com.querydsl.core.types.dsl.NumberExpression.gt(NumberExpression.java:337)
at project.dao.QuerydslKotlinCountTest.countTest(QuerydslKotlinCountTest.kt:30)
So it's not a Class<java.lang.Long> which I would expect, but some class Class<long> (never seen this before and can not get it programmatically with Class.forName("long")).
So, how do I make this simple piece of code work in Kotlin?
If I replace Long::class.java with java.lang.Long::class.java, the code does not compile:
Error:(27, 104) Kotlin: None of the following functions can be called with the arguments supplied:
public open fun `as`(p0: Path<Long!>!): NumberExpression<Long!>! defined in com.querydsl.core.types.dsl.NumberExpression
public open fun `as`(p0: String!): NumberExpression<Long!>! defined in com.querydsl.core.types.dsl.NumberExpression
The only way I made it work is using a boxed java primitive: java.lang.Long.valueOf(1).javaClass, but it looks ugly.
Try using KClass's javaObjectType property instead of java, e.g.:
1L::class.java // returns long
1L::class.javaObjectType // returns java.lang.Long
From its documentation:
[...] In case of primitive types it returns corresponding wrapper classes.
Related
FYI: I'm currently using Mockk-1.12.4 and Kotlin-1.6
I've got an extension method that returns an object of type T:
fun <T> Entity.selectReferenceAsSingleObject(referenceName: String): T {
return this.selectReferencesByName(referenceName).single().asObjet() as T
}
This is defined as top level function in an Extentions.kt file (so not a class). I'm wanting to mock this with MockK by using mockkStatic
mockkStatic(Entity::selectReferenceAsOptionalSingleObject)
However I'm getting the error:
Not enough information to infer type variable T
because it cannot work out what type I should be mocking. I've tried adding my type parameter in a bunch of places
mockkStatic<KFunction<MyType>>(Entity::selectReferenceAsSingleObject)
mockkStatic<MyType>(Entity::selectReferenceAsSingleObject)
mockkStatic(Entity<KFunction<MyType>>::selectReferenceAsSingleObject)
mockkStatic(Entity<MyType>::selectReferenceAsSingleObject)
mockkStatic(Entity::<KFunction<MyType>>selectReferenceAsSingleObject)
mockkStatic(Entity::<MyType>selectReferenceAsSingleObject)
mockkStatic(Entity::selectReferenceAsSingleObject<KFunction<MyType>>)
mockkStatic(Entity::selectReferenceAsSingleObject<MyType>)
mockkStatic(Entity::selectReferenceAsSingleObject as KFunction<MyType>)
mockkStatic(Entity::selectReferenceAsSingleObject as MyType)
But nothing works and it either tells me the same error or tells me that type arguments are not allowed there. I'm not sure what I need to do to fix my syntax as IntelliJ isn't giving me any hints so I feel kind of stuck. Any help would be appreciated.
Try this:
mockkStatic("your.package.YourFileExtensionKt")
you can also give the file a nice name:
#file:JvmName("Extension")
would look like this:
mockkStatic("your.package.Extension")
anyway for the test to work you must use a mock in the extended class, in this case Entity, it would look like this:
#Test
fun test() {
val entity : Entity = mockk(relaxed = true)
every { entity.selectReferenceAsSingleObject<Any>(any()) } returns "whatever"
val result = entity.selectReferenceAsSingleObject<Any>("test")
assertEquals("whatever", result)
}
I have this code snippet in Java (this is an MCVE; actual code is more complex but has exact same issue):
enum StatusEnum { A, B, C; }
[...]
final static Set<String> names = Arrays.asList(StatusEnum.values())
.stream().map(StatusEnum::name).collect(Collectors.toSet());
IntelliJ gave me the following automated conversion to Kotlin:
internal val names = Arrays.asList(*StatusEnum.values())
.stream().map<String>(Function<StatusEnum, String> { it.name })
.collect<Set<String>, Any>(Collectors.toSet())
This unfortunately has compile errors:
Interface Function does not have constructors
Type inference failed. Expected type mismatch: inferred type is Collector!>! but Collector!>! was expected
Unresolved reference: it
This is my very first attempt at converting some code to Kotlin. I have reviewed the Functions and Lambdas section of the documentation. Still not clear what's going on here or how to fix it.
Use Kotlin methods instead of Java streams:
val names = StatusEnum.values()
.map { it.name }
.toSet()
I am trying to understand concept of inline classes - they are a simple object wrapper of single property that is being inlined during runtime.
That means, that the actual initialization of the class is not happening at runtime
I was trying to write simple test which directly will show my above explanation during JUnit test as below:
companion object {
private const val NAME = "JACK"
}
inline class NameInlineClass(val value: String)
#Test
fun unwrapping() {
val nameInlineClass = NameInlineClass(NAME)
val name = nameInlineClass
assertEquals(name, NAME)
}
This test fails unfortunately which leads me to the question why during assertEquals() the actual unwrapped String value is not being compared, but the actual inline class (which should be unwrapped during runtime)?
What you probably wanted to do was val name = nameInlineClass.value, but I'll try to explain the error.
See Representation from docs (includes code sample):
In generated code, the Kotlin compiler keeps a wrapper for each inline
class. Inline class instances can be represented at runtime either as
wrappers or as the underlying type. This is similar to how Int can be
represented either as a primitive int or as the wrapper Integer.
That means as long as you don't reference the wrapping object or its type explicitly, value will not be boxed. We can check it by inspecting bytecode (decompiled back to Java for readability):
// kotlin source
fun unwrapping() {
val nameInlineClass = NameInlineClass(NAME)
val name = nameInlineClass // this line gets dropped by compiler by the way
assertEquals(name, NAME)
}
// java representation of bytecode
public final void unwrapping() {
String nameInlineClass = NameInlineClass.constructor-impl("JACK");
Assert.assertEquals(NameInlineClass.box-impl(nameInlineClass), "JACK");
}
I won't paste entire generated NameInlineClass body, but constructor-impl is static method that only checks for null of value, and box-impl creates the wrapper object.
You can see nameInlineClass is indeed a String - that means inline works and no extra object was allocated.
Only when you reference nameInlineClass instead of nameInlineClass.value compiler determines that this object needs representation and "boxes" the value with wrapper NameInlineClass class.
If you access a Java value of type List<[Some Type]> in Kotlin, you will get the type (Mutable)List<[Some Type]!>!.
e.g.:
Java code:
public class Example {
public static List<String> getList() {
return Arrays.asList("A", "B", "C");
}
}
Kotlin code:
val list = Example.getList()
// list is of type (Mutable)List<String!>!
Here is, how IntelliJ shows it:
However, if you want to make your own variable of this type like so:
val list2: (Mutable)List<String>
Then IntelliJ will correctly highlight the type but will give the error Unexpected Tokens.
What is this (Mutable)List?
There is no type (Mutable)List in Kotlin.
This serves as an indication that the type of list returned by Example.getList()
will not be decided at compile time but it will be decided at run time.
In your case it will be List and not MutableList because Arrays.asList() returns a FixedSizeList.
If you implemented Example.getList() like this:
public static List<String> getList() {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
return list;
}
then at runtime the type of your list would be MutableList.
It's an IDEA tool tip which shows you that this list might be as MutableList, as List, as Example is Java class and it can return any of type list.
Also, the same happens to String: you don't know anything about list's String nullability, as it is returned from Java, so String looks like String! meaning 'maybe it's null, but or maybe not' without affecting compilation (i.e. you can as invoke methods on it without null-check, as checking it on null: no warnings will appear).
MutableList is a interfece in kotlin.
To declare a variable we need to use class like as
val list2: ArrayList<String>
#Josef Zoller
Is it possible to create extension of final classes like String? Like in swift it is possible to add additional methods inside a extension of final class.
For an example - I would like to create a method in String extension which will tell me String have valid length for password.
val password : String = mEdtPassword!!.getText().toString()
// how to define haveValidLength method in extension
val isValid : Boolean = password.haveValidLength()
Note - That example is just for a sake to understand usability of extension, not a real scenario.
yes, you can. Kotin extension method provides the ability to extend a class with new functionality without having to inherit from the class or use any type of design pattern such as Decorator.
Below is an extension method for a String:
// v--- the extension method receiver type
fun String.at(value: Int) = this[value]
And the extension method code generated as Java below:
public static char at(String receiver, int value){
return receiver.charAt(value);
}
So an extension method in Kotlin is using delegation rather than inheritance.
Then you can calling an extension method like as its member function as below:
println("bar".at(1))//println 'a'
You also can write an extension method for the existing extension function, for example:
fun String.substring(value: Int): String = TODO()
// v--- throws exception rather than return "ar"
"bar".substring(1)
But you can't write an extension method for the existing member function, for example:
operator fun String.get(value: Int): Char = TODO()
// v--- return 'a' rather than throws an Exception
val second = "bar"[1]
Trying to add more detail, this answer might be helpful for someone.
Yes we can add additional methods to final classes like String. For an example I would like to add one method in String which will tell me that my String have valid number of characters for password or not.
So what I have to do is, I have ti create a below function which can be written in same class or at different separate class file.
fun String.hasValidPassword() : Boolean {
// Even no need to send string from outside, use 'this' for reference of a String
return !TextUtils.isEmpty(this) && this.length > 6
}
And now from anywhere call
val isValid : Boolean = password.haveValidLength()
Suggestion
If your application just has a single password validation, then there is no problem.
However, I don't suggest you write such a extension method hasValidPassword if the application has more than one validation. since the extension method is satically, you can't change your hasValidPassword in runtime. So if you want to change the validation in runtime, you should using a function instead, for example:
class PasswordRepository(private val validate:(String)->Boolean){
fun save(value:String){
if(validate(value)){
//TODO persist the password
}
}
}
val permitAll = PasswordRepository {true}
val denyAll = PasswordRepository {false}
permitAll.save("it will be persisted")
denyAll.save("it will not be persisted")
In other words, the extension method above violates Single Responsibility Principle, it does validation & string operations.
You can do that with extension functions in Kotlin. With extensions, you are able to add extra functionality to a class that you do or do not have access to; for example a legacy code base. In the example given in the Kotlin docs here, swap was added to MutableList<Int> which doesn't have swap originally. A this keyword is used that refers to the object that the swap functionality will operate on. In the example below, this refers to testList
val testList = mutableListOf(1, 2, 3)
testList.swap(0, 2)