TL;DR
These object : someClass{ } anonymous objects can't access itself via this (which results the outer object). How can I access it?
Longer explanation:
For my Fragment I need a PreDrawListener. I call this in onCreateView. When executing, I wanted to remove the listener afterwards. So the Java way of doing would suggest something like this
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
val treeObserver = layout.viewTreeObserver
treeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
layout.viewTreeObserver.removeOnPreDrawListener(this)
...
}
}
The problem is, when taking a look at the removeOnPreDrawListener(this) the this object is not the listener but myFragment$onCreateView$1#f019bf0
Alternatively I could access this#MyFragment which returns the reference to the Fragment directly.
Still, none of these options seems to be my PreDrawListener. How can I access it from inside (if at all)?
I honestly don't see your problem.
this inside an anonymous refers to the class itself, but they never have names. You can't create an anonymous class with a name. To demo this, I wrote some sample code:
class TheClass{
fun run(){
val anon = object: Runnable {
override fun run() {}
}
println(anon::class.java.simpleName)
println(anon::class.java.name)
}
}
Which prints:
run$anon$1
com.package.TheClass$run$anon$1
Now, this is nice and all, but it still doesn't look like yours. But you see it matches the containing class, method, variable, and finally the dollar sign denoting that it's an anonymous inner class. That applies to the second, which is the full one. The first just prints the short name, which is the method, var name, and again the dollar sign that shows it's anonymous function.
If you're interested in why the dollar sign with a number appears, see this. T
Let's expand that and ditch the variable. Obviously, this is horrible code (and far from memory-efficient, but it's a demo so it doesn't matter):
class TheClass {
fun run(){
println(object: Runnable {
override fun run() { }
})
}
}
This prints, and matching your pattern:
com.package.TheClass$run$anon$1
You've seen the pattern; now you can start "decoding" the hash you got:
myFragment // inside myFragment
$onCreateView // Inside a function
$1 // There is an anonymous class with a specific identifier
#f019bf0 // This is standard everywhere; just look up Object.toString()
What I just tried to prove is: this does refer to the anonymous function you create. Anonymous functions are, well, anonymous. They don't have names. They use $number as identifiers. So if you have this code:
treeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
layout.viewTreeObserver.removeOnPreDrawListener(this)
...
}
}
this will refer to the listener, even though printing the class may print stuff that looks confusing. If there's something that's broken, it's not because of this not referring to the listener (because it does)
Also, your code compiles fine. There's no type mismatch in there either. If it referred to a different object, it wouldn't work if you passed this to a method that requires a OnPreDrawListener.
You would get a different result with the same code in Java. This is because Kotlin compiles anonymous functions as Class$function$number, where as Java compiles it to Class$number. If it's in a nested class, it will appear as Outer$Inner$function$number in Kotlin, and Outer$Inner$number in Java.
It's a difference in the compiler that results in different names; Java excludes the function, where as Kotlin includes it. It's in the .class filename, so if you build your project and look at the file names in a file explorer for whatever OS you have (Do not look in IntelliJ. It will decompile the files for you. And remember, you're just looking for the name, which IntelliJ messes up by merging the .class files into a single one to match the original source)
Just as final meta, I do print the class instead of printing the object. Interfaces do not have an overridden toString method, which means it defaults to the one on Object, which returns getClass().getName() + "#" + Integer.toHexString(hashCode()); (original code can be found here). println(this) is the same as println(this.toString()), which calls the toString method in Object, which prints the class name. println(this) is the same as printing the object or printing the class
Related
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
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>()
Today while programming I found some odd behaviour in Kotlin. I could easily go around it, but I wonder if there is some reason to it or if it is a bug in Kotlin.
I have the following interface of a delegate which delegates the showing of a dialog to the Activity.
interface ViewModelDelegate {
fun showWarningDialog(textResource: Int)
}
I want to implement it as following in the Activity. Since I know I can only do it with a context and the Activity.getContext() may return null, I wrap the code in context?.let
override fun showWarningDialog(textResource: Int) = context?.let {
//show dialog
}
However this gives me a compile error:
Return type of 'showWarningDialog' is not a subtype of the return type of the overridden member 'public abstract fun showWarningDialog(textResource: Int): Unit defined in com.some.class.path'
Which really confused me, because I don't want to return anything. So since let returns whatever the function inside returns, I was wondering if I could fix it by writing a version of let which does not return anything.
fun <T, R> T.myLet(block: (T) -> R) {
let(block)
}
However this did not remove the compiler error. I found then that the mouseover text over the error gives more information (would be nice if the compiler did). It says:
Return type is 'Unit?', which is not a subtype of overridden
Now that tells me more about the problem. Because the function context?let call may not happen, it could return null. Now there are multiple ways to go around this. I could add ?: Unit too the end of the function call or I could define showWarningDialog to return Unit? which will allow me to call it just fine in most cases. However none of these solutions are desireable. I will probably just make a normal method and call the let inside of that instead of delegating the call to it. Costs me another level of indentation and an extra vertical line:
override fun showWarningDialog(textResource: Int) {
context?.let {
//show dialog
}
}
My question is, is this behaviour intended? Why or when would this be useful that a function that returns Unit cannot be delegated to an optional function call. I am very confused by this behaviour.
Single expression function
fun foo() = <expression>
by language design is equivalent to
fun foo(): <ReturnType> {
return <expression>
}
And because Unit? is not a not a subtype of Unit, you can't return it in from a function, which returns Unit. In this sense Unit just another type in the type system, it's not something magical. So it works just as it's supposed to work with any other type.
Why or when would this be useful that a function that returns Unit cannot be delegated to an optional function call.
So basically the question is why language designers did not created a special handling to accept Unit? from a function declaring Unit as a return type. I can think about a few reasons:
It requires to create this special handling in the compiler. Special cases lead to bugs, break slim language design and complicate documentation.
As it had to be a special case, it would be not really clear and predictable for programmers. Currently it works in the same way for all types, no special treatments. It makes the language predictable, you don't need to check the documentation for every type to see if it's treated specially.
It also adds some additional safety, so to make you notice that your expression can actually skip the calculation.
So trying to summarize, I would say making this case work does not add much of value but can potentially bring some issues. That's probably why they did not add it to the language.
lets discuss this case when you have return type for example String
interface someInterface{
fun somFun():String
}
class someClass : someInterface {
var someString:String? = null
override fun somFun()=someString?.let {
//not working
it
}
override fun somFun()=someString?.let {
//working
it
}?:""
}
so what we see that when parents return type is String you cannot return Strin? it is jus kotlins nullSafety ,
what is different when you don't have return type ? lets change the code above a little
interface someInterface{
fun somFun():String
fun unitFun()
}
class someClass : someInterface {
var someString:String? = null
override fun unitFun() {
//if it is possible to return null in here
}
override fun somFun()=someString?.let {
val someresult = unitFun().toString() //you will get crash
it
}?:""
}
now we have another function without return type (unitFun Unit)
so if you can return Unit? in your subclass it will cause a crash when you want to use the result of method because it is defined asUnit and you dont need any null checks.
generally it means Unit is also type and you need to keep it null safe .
Android Studio suggests to replace anonymous inner class with lambda.
titleTextView.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
Log.d("MY_TAG", "textView clicked in anonymous inner class")
}
})
Decompiled Java code:
var10000 = this.titleTextView;
if (this.titleTextView == null) {
Intrinsics.throwUninitializedPropertyAccessException("titleTextView");
}
var10000.setOnClickListener((OnClickListener)(new OnClickListener() {
public void onClick(#Nullable View v) {
Log.d("MY_TAG", "textView clicked in anonymous inner class");
}
}));
Before lambda, to avoid creation of new object for each of views that were set OnClickListener, it was better to have Activity/Fragment implement View.OnClickListener interface or use Butterknife's #OnClick annotation.
How different the performance will be with lambda like below?
titleTextView.setOnClickListener { Log.d("MY_TAG", "textView clicked in lambda") }
Decompiled Java code:
TextView var10000 = this.titleTextView;
if (this.titleTextView == null) {
Intrinsics.throwUninitializedPropertyAccessException("titleTextView");
}
var10000.setOnClickListener((OnClickListener)null.INSTANCE);
In case of lambda I don't see the Log.d("MY_TAG", "textView clicked in lambda") in decompiled code.
The performance of a lambda will be at least as good as creating an anonymous inner class.
If it doesn't capture any references, the compiler will make it a singleton inside whatever class it's used in as an optimization measure. This is what happens in your case, as the contents of your listener don't refer to anything outside of the lambda. (This singleton instance is what the null.INSTANCE is trying to refer to, the decompiler just has trouble with resolving the name of the class generated for the lambda.) So in this case, the cost of the lambda is just 1 object allocation.
If your lambda does capture something, e.g. like this:
val random = Random().nextInt()
titleTextView.setOnClickListener {
Log.d("MY_TAG", "textView clicked in lambda, random value was $random")
}
... then whenever you set the listener, a new instance will have to be allocated, because these instances have to store references to variables, which might be different each time they're created. In this case, you get as many object allocations as many times you run the method the lambda's in. Note that if this is only done during setup, like in onCreate, this will mean just 1 object allocation as well.
So you may get:
0 extra allocations, if your listeners are methods of your already existing class (Fragment or Activity).
1 extra allocation if you use a lambda that doesn't capture anything.
N extra allocations if you use a lambda that captures something, N being the number of times you run the code where the lambda is used, which could be 1 if this is only during initialization.
N extra allocations if you use an anonymous inner class, as there's no optimization there for non-capturing classes. Again, this may actually be 1 in a lot of cases.
Even though using methods inside the existing class would mean 0 additional allocations, I wouldn't go with that approach for performance - any gains will probably be completely unnoticeable. Go with the solution that's more readable and maintainable for you instead.
In the last year I've become a mobile developer and a functional programming admirer.
In each of the mobile arenas there are components with lifecycle methods that make up the meat of the app. The following will use Android and Kotlin as examples, but the same applies to iOS and Swift.
In Android, there are Activity's with lifecycle methods like onCreate(). You might also define a function, onButtonClicked(), which will do exactly what the name describes.
For the purposes of the question, let's say there's a variable defined in onCreate() that is used in a button click handler onButtonClickedPrintMessageLength() (This is usually the case - onCreate() is essentially Activity's setup method).
The example class would look like this:
class ExampleActivity: Activity() {
var savedStateMessage: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedStateMessage = "Hello World!"
}
fun onButtonClickedPrintMessageLength() {
System.out.println(savedStateMessage?.length)
}
}
Notice the declaration of savedStateMessage as a String? (nullable string) and the use of ?. (null safe call). These are required because the compiler cant guarantee that onCreate() will be called before onButtonClickedPrintMessageLength(). As developers though, we know that onCreate will always be called first* **.
My question is how can I tell the compiler about the guaranteed order of these methods and eliminate the null checking behavior?
* I suppose it's possible to new up our ExampleActivity and call onButtonClickedPrintMessageLength() directly, thus sidestepping the Android framework and lifecycle methods, but the compiler/JVM would likely run into an error before anything interesting happened.
** The guarantee that onCreate is called first is provided by the Android framework, which is an external source of truth and might break/function differently in the future. Seeing that all Android apps are based on this source of truth though, I believe it's safe to trust.
Although this won't answer your actual question, in Kotlin you can use lateinit to tell the compiler that you'll initialize a var at a later point in time:
lateinit var savedStateMessage: String
You'll get a very specific UninitializedPropertyAccessException if you try to use this variable before initializing it. This feature is useful in use cases like JUnit, where you'd usually initialize variables in #Before-annotated method, and Android Activitys, where you don't have access to the constructor and initialize stuff in onCreate().
As mentioned in another answer, lateinit is available as an option to defer initialization to a later point in a guaranteed lifecycle. An alternative is to use a delegate:
var savedStateMessage: String by Delegates.notNull()
Which is equivalent, in that it will report an error if you access the variable before initializing it.
In Swift this is where you would use an implicitly-unwrapped Optional:
class Example: CustomStringConvertible {
var savedStateMessage: String! // implicitly-unwrapped Optional<String>
var description: String { return savedStateMessage }
init() {
savedStateMessage = "Hello World!"
}
}
print(Example()) // => "Hello World!\n"
By using the operator ! at the end of String in the second line of the example you are promising that the variable will be set before it can be used. This is accomplished in the init method of the example. It's still an Optional but code can treat it as a String since it will be automatically unwrapped before each use. You must take care that the variable is never set to nil when it might be accessed or a runtime exception may be generated.