what does "::" mean in kotlin? - android

I'm new to Kotlin
I used this code for opening another activity:
startActivity(Intent(this,IntroAndLang::class.java))
current activity and target activity are written in Kotlin
I can't understand why there is not single : instead of :: at IntroAndLang::class.java

:: is used for Reflection in kotlin
Class Reference val myClass = MyClass::class
Function Reference this::isEmpty
Property Reference ::someVal.isInitialized
Constructor Reference ::MyClass
For detailed reading Official Documentation

:: converts a Kotlin function into a lambda.
Let's say you have a function that looks like this:
fun printSquare(a: Int) = println(a * 2)
And you have a class that takes a lambda as a 2nd argument:
class MyClass(var someOtherVar: Any, var printSquare: (Int) -> Unit) {
fun doTheSquare(i: Int) {
printSquare(i)
}
}
How do you pass the printSquare function into MyClass? If you try the following, it wont work:
MyClass("someObject", printSquare) //printSquare is not a LAMBDA, it's a function so it gives compile error of wrong argument
So how do we CONVERT printSquare into a lambda so we can pass it around? Use the :: notation.
MyClass("someObject",::printSquare) //now compiler does not complain since it's expecting a lambda and we have indeed converted the `printSquare` FUNCTION into a LAMBDA.
Also, please note that this is implied... meaning this::printSquare is the same as ::printSquare. So if the printSquare function was in another class, like a Presenter, then you could convert it to lambda like this:
Presenter::printSquare
UPDATE:
Also this works with constructors. If you want to create the constructor of a class and then convert it to a lambda, it is done like this:
(x, y) -> MyClass::new
this translates to MyClass(x, y) in Kotlin.

As stated in the docs this is a class reference:
Class References:
The most basic reflection feature is getting the runtime reference to a Kotlin class. To obtain the reference to a statically known Kotlin class, you can use the class literal syntax:
val c = MyClass::class
//The reference is a value of type KClass.
Note that a Kotlin class reference is not the same as a Java class reference. To obtain a Java class reference, use the .java property on a KClass instance.
It’s also the syntax for method references as in this simple example:
list.forEach(::println)
It refers to println defined in Kotlin Standard library.

Since kotlin 1.1, in addition to class, function, property and constructor references as stated above, '::' can also be used to obtain the bound references to all of the above.
For instance, using '::class' could be used to get the exact class of a particular object despite the type of the receiver as below...
val widget: Widget = ...
assert(widget is GoodWidget) { "Bad widget: ${widget::class.qualifiedName}" }
widget::class returns the exact class of the object 'widget' as either 'GoodWidget' or 'BadWidget' despite the type of the receiver expression (i.e 'Widget' as declared initially)
More info at https://kotlinlang.org/docs/reference/reflection.html

Related

Kotlin - Issue Extending Kotlin String Class

I am currently trying to extend Kotlins String class with a method in a file StringExt.kt
fun String.removeNonAlphanumeric(s: String) = s.replace([^a-ZA-Z0-9].Regex(), "")
But Kotlin in not allowing me to use this method in a lambda:
s.split("\\s+".Regex())
.map(String::removeNonAlphanumeric)
.toList()
The error is:
Required: (TypeVariable(T)) -> TypeVariable(R)
Found: KFunction2<String,String,String>
What confuses me about this is that Kotlins Strings.kt has very similar methods and
I can call them by reference without Intellij raising this kind of issue. Any advice is appreciated.
This is because you have declared an extension function that accepts an additional parameter and should be used as s.replace("abc").
I think what you meant is the following:
fun String.removeNonAlphanumeric(): String = this.replace("[^a-ZA-Z0-9]".toRegex(), "")
This declaration doesn't have an extra parameter and uses this to refer to the String instance it is called on.
I thing this is because a lambda is an anonymous function and dose not access to the scope of a extension file.
Check this link maybe contains some useful information:
https://kotlinlang.org/docs/reference/extensions.html

How to create interchangeable class types for android fake testing?

I'm trying to create a fake class for my repository to test a view model.
As far as I understood, the key element here is to create two classes with a common interface so both classes would contain the same methods.
The problem is I get a Type mismatch when trying to initialize an object.
I tried to do the same in a simplified manner:
class fakeClass1 : fakeInterface {
override fun getAllData(): String {
return ""
}}}
class fakeClass2 : fakeInterface {
override fun getAllData(): String {
return ""
}}
interface fakeInterface {
fun getAllData(): String}
val fakeClass: fakeClass1 = fakeClass2()
But that didn't work either.
What am I missing?
Ok, I figured it out.
I was wrong to think that those two classes should be interchangeable.
I solved it by making the ViewModel take the common interface in its constructor instead of the actual repository class. This allows the ViewModel to take any class which implement this interface as it's repository.
I think you worked it out, but just so you're clear (this is an important, fundamental thing!)
val fakeClass: fakeClass1 = fakeClass2()
This is defining a variable called fakeClass that refers to an object with the fakeClass1 type. Then you assign an object with the fakeClass2 type.
But a fakeClass2 is not a fakeClass1, neither is a superclass of the other, so you can't treat one as the other. Your example is simple, but imagine you added coolFunction() to fakeClass1 - they'd now happen to have different structures, and trying to call that method on an object that doesn't have it would cause a crash.
The only thing those classes have in common, is that they both have the fakeInterface type - they are fakeInterfaces, and that guarantees they implement the stuff in that interface (your getAllData function in this case). So if you treat them both as that type instead:
val fakeClass: fakeInterface = fakeClass2()
you can use either one, because they're both fakeInterfaces (similar to how Ints and Doubles are different but they're both Numbers). Because fakeClass is now a fakeInterface, you can only access the functions and properties that a fakeInterface has - you can't call coolFunction() even if you happened to pass in a fakeClass1, because fakeInterface doesn't have that.
(You could cast the variable to fakeClass1, basically saying "oh by the way this object is actually this type as well", but at that point the type system can't guarantee you're correct unless you're explicitly checking fakeClass is fakeClass1, and it'll warn you if that's the case)
The Java tutorials are pretty good and they'll give you an overview about how the types each form a kind of "contract" you work with

How to use a Kotlin extension function on a class?

I have a pretty short question about an extension function that would help clear some of my code. Basically I have some transformations on the hashCode of a class name and I want an extension function to do the transformations.
Example:
Getting the name hashCode: StateA::class.java.name.hashCode() where StateA is a simple class.
I want to the extension function like:
fun Class<*>.transformName(): String {
var hashString = this.javaClass.name.hashCode()
//Do my stuff on that hashString
return hashString
}
But this doesn't seem to work. When I apply the extension function with StateA.transformName(), the function gives me an error with Unresolved Reference.
I tried various things like applying the function to StateA::class or having the hashString equal to this::class.java.name.hashCode() but nothing works. Any tips?
You can't really achieve the StateA.transformName() syntax, as StateA just on its own refers to the companion object inside that class. So to get that syntax, you'd need to have a companion object inside every class that you want to use this extension on.
What you can do in a very general way is get the KClass that describes your class first. This gives you an object (the KClass instance) that you can then call an extension on:
fun KClass<*>.transformName() {
val clazz: Class<*> = this.java
clazz.name.hashCode()
}
StateA::class.transformName()
Another approach, which is less verbose on the call site could be a generic function like this, where the reified keyword allows you to access the concrete class that was used as the generic type parameter inside the function:
inline fun <reified T> transformName() {
val clazz: Class<*> = T::class.java
clazz.name.hashCode()
}
transformName<StateA>()

Inferring parameter of class type with default

I have a class with constructor declared like this
class Facade<T : SuperClass>(
val kClass: KClass<in T> = SuperClass::class
)
It is done like this so that developer doesn't have to specify SuperClass if they want to use it instead of a subclass.
Reason to even send the class type is, so that developer doesn't have to specify type in angle brackets .
But now comes the problem. Creating instance like below, says that there is not enough information to infer parameter T. Which is resulting in having to put class into angle brackets.
Facade()
But since default value is SuperClass, then kotlin should be able to infer parameter T as SuperClass. What am I thinking wrong?
Thank you
TL;DR:
Facade(SubClass:class) // working
Facade(SuperClass:class) // working, but don't want (superclass is default)
Facade<SuperClass>() // working, but don't want angle brackets <>
Facade() // not working, cannot infer T type from default, why?
For the Facade() to be equivalent to Facade(DerivedClass::class) the default constructor parameter would have to be declared as val kClass: KClass<in T> = T::class. However to use T::class the T type parameter would need to be reified. A type parameter can only be reified in inline functions and not constructors.
To workaround this issue you can declare a factory function that delegates to constructor like so:
inline fun <reified T : SuperClass> Facade(): Facade<T> = Facade(T::class)
This allows one to use it as e.g.:
val derivedFacade:Facade<DerivedClass> = Facade()
Note that if you would like to use the SuperClass as the default parameter for T you would need to declare another factory method using different name e.g.:
fun SuperFacade(): Facade<SuperClass> = Facade()
This is required since if we had declared #JvmName("SuperFacade") fun Facade() = Facade(SuperClass::class) the compile would match it whenever we do not provide type parameters. This in turn would defy the type inference from the derivedFacade example.
You can resolve your problem by removing the angle brackets and changing the constructor type.
Just use this constructor:
class Facade(val kClass: KClass<*> = SuperClass::class)
All these call are working
Facade(SubClass:class)
Facade(SuperClass:class)
Facade()

Android Studio (Kotlin) GsonBuilder.registerTypeAdapter() gives 2 errors

I am using the Kotlin plugin and trying to create a gson variable using GsonBuilder.
This used to work without problems in Java, but now in I get the two errors when trying to use registerTypeAdapter(), as shown below:
val gson = GsonBuilder().registerTypeAdapter(DateTime.class, DateTimeTypeConverter()).create()
For the first parameter (type), I get "name expected" error.
For the second parameter (typeAdapter), I get "expecting an expression" error
DateTime.class should be changed to Date::class.java
Maybe it will resolve your second issue as well, otherwise please post your DateTimeTypeConverter source code
Class References
The most basic reflection feature is getting the runtime reference to
a Kotlin class. To obtain the reference to a statically known Kotlin
class, you can use the class literal syntax:
val c = MyClass::class The reference is a value of type KClass.
Note that a Kotlin class reference is not the same as a Java class
reference. To obtain a Java class reference, use the .java property on
a KClass instance.
Reference: https://kotlinlang.org/docs/reference/reflection.html

Categories

Resources