I convert one of my Java class to Kotlin and the class as below.
class MainApplication : Application() {
companion object {
operator fun get(context: Context): MainApplication {
return context.applicationContext as MainApplication
}
}
}
It has a static function get.
I still have a Java function accessing it.
MainApplication application = MainApplication.get(mContext);
It was good when MainApplication is in Java. But not when MainApplication in Kotlin, the above code error
Error:(27, 54) error: cannot find symbol method get(Context)
How could I access get in my Java code above?
You can add #JvmStatic annotation to the method in companion object to make Kotlin generate a static method.
class MainApplication : Application() {
companion object {
#JvmStatic fun get(context: Context): MainApplication {
return context.applicationContext as MainApplication
}
}
}
you can then access it from Java like before converting to Kotlin:
MainApplication application = MainApplication.get(mContext);
EDIT: I feel obliged to add something I learned recently: #JvmStatic doesn't actually move where the method gets generated. It duplicates it, by generating a static method for Java in addition to the method on the companion object. Personally I think this isn't great and it can have some implications depending on a use case, so something worth knowing.
Ops, I got it. Just use the below.
MainApplication application = MainApplication.Companion.get(mContext);
By omitting the name of your companion object, the name Companion must be used to access the methods.
Example:
class MyClass1 {
companion object Object1 {
fun method1 {
}
}
}
class MyClass2 {
companion object {
fun method2 {
}
}
}
To invoke the first companion object method you would do the following:
MyClass1.method1()
To invoke the second:
MyClass2.Companion.method2()
See the Kotlin docs on Companion Objects for details.
You may encounter a problem where you cannot access the Companion object's method in Java if the new keyword is used in the method call. The new keyword should be omitted. The documentation states:
Companion objects and their members can only be accessed via the containing class name, not via instances of the containing class.
So if you have a class like this:
class MyClass {
companion object {
fun create() {}
}
}
You can call the companion object's method like this:
MyClass.create()
But not like this:
new MyClass.create
Related
I am about to make a service of mine generic. However I fail to do so when trying to pass a generic Kotlin type T to a Java method that expects a class. Using normal types I'd do it like MyClass::class.java. For the generic type I do T::class.java. This however seems not to be valid.
Cannot use 'T' as reified type parameter. Use a class instead.
Happening here return mongoTemplate.aggregate(resolvedDocument, T::class.java).mappedResults[0]
Service:
#Service
class DocumentAggregator<T: Output>(
#Autowired
private val mongoTemplate: MongoTemplate
) {
fun <S: DocumentEntity>aggregate(document: S): T? {
val resolvedDocument: TypedAggregation<DocumentEntity> = // logic
return mongoTemplate.aggregate(resolvedDocument, T::class.java).mappedResults[0]
}
}
You should try adding the reified keyword to the generic parameter, like this:
class DocumentAggregator<reified T: Output>
That ways the class will be present at runtime. Like when you added an additional Class<T> parameter, just with the nice Kotlin syntax sugar.
EDIT:
Regarding the comments the question would be if you need the generics on the class. What compiles (thanks to Willie for pointing out the mistake) would be:
class Output
class DocumentAggregator(
private val mongoTemplate: Any?
) {
inline fun <S, reified T: Output>aggregate(document: S): T? {
return null
}
}
I want to have a class that can only be instantiated by another class. I know I have to make the constructor private otherwise everyone can instantiate it.
class Root private constructor() {
}
class RootMaker: Application() {
fun createRoot() = Root()
} // but this doesn't work because Root's constructor is private
one workaround is to make the maker class, the inner class of the Root class.
class Root private constructor() {
class RootMaker: Application() {
fun createRoot() = Root()
}
}
but I really don't want to do this because the maker class is my application class in android. so what is the better way?
If you want only one instance of an object you can use object keyword in Kotlin. It implements Singleton pattern:
class App : Application {
val root = Root
}
object Root {
fun createObject(): Any {}
}
Now we can access to one instance of Root class either by a property in App class or via Root class: Root.createObject()
UPDATE:
To implement a singleton that only one specific class has access to, we can use an interface and hide its implementation in that specific class (the maker class):
interface IRoot {
// ... methods of creation different objects for dependency injection
}
class App : Application {
val root: IRoot = Root
// hide implementation of `IRoot` interface in `App` class
private object Root : IRoot {
// ... implementation of methods of creation different objects for dependency injection
}
}
First Post. Absolute Beginner. Be kind
I am playing arround with quarkus and kotlin.
I have this kotlin entity class:
#Entity
data class Fruit (
var name: String = "",
var description: String = ""
) : PanacheEntity()
I have this Resource Class based on tutorials in Java:
#Path("/fruits")
#ApplicationScoped
public class FruitJResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Fruit> getAll() {
return Fruit.listAll();
}
}
Everything fine here, Fruit inherits from PanacheEntityBase, i can access listAll()
However,
Same Class in Kotlin does not:
#Path("/fruits")
#ApplicationScoped
class FruitResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
fun getAll(): List<Fruit> = Fruit.listAll()
}
Now i learned allready, that this is probably due kotlin not beeing able to inherit static methods from Super Class.
I read, that i should call the static method direct from the superclass, but this won't work here.
So I need a suggestion for a possible workaround.
The only solution for kotlin and scala languages for now (1.4.1) is to use the Repository pattern :
see documentation: https://quarkus.io/guides/hibernate-orm-panache#solution-2-using-the-repository-pattern
This is due to referenced issue github.com/quarkusio/quarkus/issues/4394.
So if using Kotlin, you simply have to define a new FruitRepository
#ApplicationScoped
class FruitRepository: PanacheRepository<Fruit> {
fun all(): List<Fruit> = findAll(Sort.by("name")).list<Fruit>()
}
Quarkus has released an extension which brings Kotlin support to panache (I think it is still in preview).
in Gradle (if you're using Gradle for your project) you'll need to add the dependencie implementation 'io.quarkus:quarkus-hibernate-orm-panache-kotlin'
To define your "static" methods (Kotlin uses Companion objects to do stuff with static methods) you'll need to define a companion object like this:
#Entity
open class Category : PanacheEntityBase {
#Id
#GeneratedValue
lateinit var id: UUID
// ...
companion object : PanacheCompanion<Category, UUID> {
fun findByName(name: String) = find("name", name).firstResult()
fun findActive() = list("active", true)
fun deleteInactive() = delete("active", false)
}
}
for more information you can check out the official docs:
https://quarkus.io/guides/hibernate-orm-panache-kotlin
Be warned if you use unit tests: at least for me the panache-mock extension is not working with the kotlin version of panache.
I faced the following error trying to extend RuntimeException and implement GraphQLError interface, defined in Java, from my Kotlin code. This is the error:
Accidental override: The following declarations have the same JVM signature (getMessage()Ljava.lang.string;):
public open fun <get-message>(): String? defined in NegativeCountException
public open fun getMessage(): String? defined in NegativeCountException
The following is my code:
class NegativeCountException() : RuntimeException(), GraphQLError {
override fun getMessage(): String? {
TODO("not implemented")
}
<...>
}
where GraphQLError is an interface, defined in Java as shown bellow:
public interface GraphQLError {
String getMessage();
<...>
}
Seems like it clashes with getMessage() defined in Throwable.
I can't change code of the interface, because it comes from a library.
How can I create my own runtime exception, that will implement GraphQLError?
PS: I also tried the following, and received a very similar error:
class NegativeCountException(override val message: String?) : RuntimeException(), GraphQLError {
<...>
}
This is a graphql problem. My workaround:
Reimplement GraphQLError, ExceptionWhileDataFetching and DataFetcherErrorHandler.
KGraphQLError is a "fixed" kotlin interface (with val instead of getters) that you use for your custom errors.
in KDataFetcherErrorHandler: Replace ExceptionWhileDataFetching in this line with KExceptionWhileDataFetching:
val error = ExceptionWhileDataFetching(path, exception, sourceLocation)
KExceptionWhileErrorHandling implements GraphQLError. Look through the code and replace all instances of if (exception is GraphQLError) with (exception is KGraphQLError)
Pass the new KDataFetcherErrorHandler to your queryExecutionStrategy and mutationExecutionStrategy.
Your custom errors can now extend Throwable and implement KGraphQLError, and get handled properly.
More info here: http://graphql-java.readthedocs.io/en/latest/execution.html
I think it would work, when message was not a field in the one class (Throwable) and a method in the other. But it seems kotlin can not resolve the ambiguity when message is a field in the class and a method in the interface. If you have control over your GraphQlError you could do this:
class NegativeCountException() : RuntimeException(), GraphQLError {
override val message: String?
get() = super.message
}
interface GraphQLError {
val message: String?
}
This is because Kotlin will create getters and setters for your variables when generating Java byte code. For example,
class Foo(val bar)
Foo with have getBar in Java byte code. To avoid name conflict, use #JvmField and #JvmName.
Read more about this here.
There is a quick and dirty workaround, in case your project allows Java alongside Kolin.
Write your NegativeCountException in Java :D
There's actually a good solution for this (provided here):
class NegativeCountException(#JvmField override val message: String) : RuntimeException(message), GraphQLError {
override fun getMessage(): String? = super.message
<...>
}
Wanted to know if it is possible to invoke a controller and return a view from a normal Java File.
For Example:
#Controller
class ToBeInvokedController {
#RequestMapping(value="/invoke")
public String invokedMethod() {
return "view_name";
}
}
class DemoJava{
//Want to Invoke the method InvokedMethod() of ToBeInvokedController class and return the view_name
}
Just Create an object of it and invoke it like normal method.
Like :
class DemoJava{
ToBeInvokedController c = new ToBeInvokedController();
String s = c.InvokedMethod();
System.out.print(s)
}
Wanted to know if it is possible to invoke a controller and return a
view from a normal Java File.
Yes, you can but you need to instantiate the ToBeInvokedController class Since it's a non-static method.
ToBeInvokedController obj = new ToBeInvokedController();
obj.InvokedMethod();
if it would have been a static method you can invoke it directly via
ToBeInvokedController.InvokedMethod();
I strong suggest you to use camelCase convention when naming methods in Java