On https://github.com/mozilla/rust-android-gradle/blob/8183f9e927336011c7c09d75efd4f5f411940db1/plugin/src/main/kotlin/com/nishtahir/CargoBuildTask.kt#L19 we have this kotlin code:
open class CargoBuildTask : DefaultTask() {
var toolchain: Toolchain? = null
#Suppress("unused")
#TaskAction
fun build() = with(project) {
extensions[CargoExtension::class].apply {
I'm very confused as from where does extensions come from, as well as project. They aren't local variables, they aren't in a scope or something. What are they?
Inheritance is the answer to project.
CargoBuildTask : DefaultTask()
Do you see how CargoBuildTask inherits properties of DefaultTask?
Thus project is a property from DefaultTask. However, extensions is a property from project which is of Type Project.
Read this documentation on DefaultTask and you will have more understanding.
Kotlin's with is the answer to extensions.
In short, with with you can invoke methods without explicitly stating its subject. (Read more here)
For example, these two code snippets mean the exact same thing:
with("string") {
substring(3) //invoke method without subject
}
"string".substring(3) //Same as above
Here is the method from org.gradle.api.Project
ExtensionContainer getExtensions();
Now, If you are wondering how Java's getExtensions() turned into Kotlin's extensions, read this. Basically states that traditional Getters and Setters in Java are interpreted as Properties in Kotlin.
PS: If you are unsure of what Inheritance is in OOP/Kotlin, read this.
The projectcomes from the DefaultTask() inherited in the current class. Inheritance is used here which is a very basic concept. Read more about Kotlin's inheritance here.
The extensions comes from project using with which is one of the scope functions.
Read more about with here.
.
For example. Suppose you've a Data class.
data class PersonModel(val name: String, var age: Int)
And you create a model of it as
val personModel = PersonModel("Adam", 30)
Now, if you pass it to a with function as a reciever, you can access 'personModel`'s properties directly in the with's scope as:
with(personModel) {
//name is the property of personModel
val nameWas = name
//Declared var and can be editable.
age = 31
}
with works with functions as well where you can pass a function as a reciever to it and it returns the returned value of the function.
These scope functions (let, run, with, apply, also) are extremely useful in production environment.
Related
I have lots of external classes (generated externally; not under my control), which do not come with a builder and which are rather cumbersome to create. However using apply it is rather easy to build them, e.g.:
SomeOfTheObjects().apply {
someProperty = SomeOtherComplexObject().apply {
someOtherProperty = "..."
}
}
Now I like the way it works with the receiver, but I would like to prevent that I can set someProperty within SomeOtherComplexObject. If the classes were under my control, it would suffice to put a #DslMarker on that class, but as they aren't, the only other way that came to my mind, was to use also instead without renaming the parameter, e.g.:
SomeOfTheObjects().also {
it.someProperty = SomeOtherComplexObject().also {
it.someOtherProperty = "..."
//it.someProperty will not work if SomeOtherComplexObject has no such property
}
}
While it works, it now has tons of it. in the code and I was wondering, whether it is possible to have some similar behaviour as with the #DslMarker in place.
What I tried is a mixture of the following:
#DslMarker
annotation class DemoMarker
#DemoMarker
inline fun <T> T.build(#DemoMarker builder : T.() -> Unit) = this.apply(builder)
"mixture", because I ended up putting the annotation everywhere, but this doesn't have any effect. If I put it on a class it works as expected. Did I miss something and it is actually possible somehow? Or does anyone have an appropriate workaround for this, besides using also?
For third party classes you can use the DslMarker annotation on receiver types as explained here.
#DslMarker
#Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class TestDsl
fun build1(builder: (#TestDsl DslReceiver1).() -> Unit) {}
Code
import kotlin.reflect.full.*
class FooBar(val bar: String)
fun FooBar.baz(): Unit {println(this.bar)}
fun main(args: Array<String>) {
FooBar::class.declaredMemberExtensionFunctions.forEach {
println(it)
}
FooBar::class.memberExtensionFunctions.forEach {
println(it)
}
}
Output is empty
This is because declaredMemberExtensionFunctions only returns extension functions that are declared inside a class (as seen in the docs) and FooBar.baz() is a top level declaration (So it is not declared inside FooBar.
class FooBar(val bar: String) {
fun FooBar.baz(): Unit {
println(this.bar)
}
}
While I imagine this is not what you want, structuring the extension function like this would make your main method output lines.
TLDR: You aren't going to be able to do this. Because extension functions can be declared everywhere, you are limited in what the reflection system can do for you.
There is a thread on kotlinlang.org that covers this exact question and why it is not possible.
Essentially, Kotlin's declaredMemberExtensionFunctions function is able to list extension functions which are declared as part of the class, not externally. The docs state:
Returns extension functions declared in this class.
And of course, memberExtensionFunctions behaves similarly:
Returns extension functions declared in this class and all of its superclasses.
Here's what #Yole says in that thread as to why this is not possible:
The task of finding all extension functions for Foo is equivalent to finding all methods which have Foo as the first parameter. Neither of these is possible without accessing every single class in your application through reflection.
#Yole is on here, he might be able to provide a more authoritative answer for you.
Is it possible to create an extension function and call it as if it were static?
For Example...
fun System.sayByeAndExit() {
println("Goodbye!")
System.exit()
}
fun main(args: Array<String>) {
System.sayByeAndExit() // I'd like to be able to call this
}
I know that the code sample doesn't work...
I understand that kotlin's extension functions are resolved statically, as mentioned in the Kotlin Reference (Extension Functions), but this does not mean they can be called as if they were static functions within a class (in a Java sense).
I also understand that this code will not work because there is no instance of System to pass into the method that the compiler will generate; therefore it won't compile.
Why would I want this?
Some of you might be wondering why this behaviour is desirable. I can understand why you would think that is isn't, so here are some reasons:
It has all of the benefits that standard extension functions give.
An instance of the class doesn't need to be created just to access the extra functionality.
The functions can be accessed from an application-wide context (provided the class is visible).
To summarise...
Does Kotlin have a way to "hook" a static function onto a class? I'd love to know.
You are really asking for "extension functions for a Class reference" or "adding static methods to existing classes" which was covered by another question here: How can one add static methods to Java classes in Kotlin which is covered by a feature request KT-11968
Extension functions cannot be added to anything that does not have an instance. A reference to a Class is not an instance and therefore you cannot extend something like java.lang.System. You can however extend a companion object of an existing class. For example:
class LibraryThing {
companion object { /* ... */ }
}
Allows you to extend LibraryThing.Companion and therefore calling some new myExtension() method would look like you are extending the Class reference itself, when really you are extending the singleton instance of the companion object:
fun LibraryThing.Companion.myExtension() = "foo"
LibraryThing.Companion.myExtension() // results in "foo"
LibraryThing.myExtension() // results in "foo"
Therefore you might find some Kotlin libraries add empty companion objects just for this case. Others do not, and for those you are "out of luck." Since Java does not have companion objects, you cannot do the same for Java either.
The other commonly requested feature is to take an existing Java static method that accepts an instance of a class as the first parameter, and make it behave as an extension function. This is tracked by issues KT-5261, KT-2844, KT-732, KT-3487 and probably other feature requests.
You can define extension function for an object and use it from system-wide context. An object will be created only once.
object MyClz
fun MyClz.exit() = System.exit(0)
fun main(args: Array<String>) {
MyClz.exit()
}
Or
class MyClz {
companion object
}
fun MyClz.Companion.exit() = System.exit(0)
fun main(args: Array<String>) {
MyClz.exit()
}
Kotlin allows to name a function same as an existing class, e.g. HashSet with initializer function could be implemented like this:
fun <T> HashSet(n : Int, fn: (Int) -> T) = HashSet<T>(n).apply {
repeat(n) {
add(fn(it))
}
}
When used, it looks like a normal HashSet constructor:
var real = HashSet<String>()
var fake = HashSet(5) { "Element $it" }
Should this be avoided or encouraged and why?
UPD
In the updated coding conventions, there's a section on this topic:
Factory functions
If you declare a factory function for a class, avoid giving it the same name as the class itself. Prefer using a distinct name making it clear why the behavior of the factory function is special. Only if there is really no special semantics, you can use the same name as the class.
Example:
class Point(val x: Double, val y: Double) {
companion object {
fun fromPolar(angle: Double, radius: Double) = Point(...)
}
}
The motivation I described below, though, seems to still hold.
As said in documentation about the naming style:
If in doubt default to the Java Coding Conventions such as:
methods and properties start with lower case
One strong reason to avoid naming a function same to a class is that it might confuse a developer who will use it later, because, contrary to their expectations:
the function won't be available for super constructor call (if the class is open)
it won't be visible as a constructor through reflection
it won't be usable as a constructor in Java code (new HashSet(n, it -> "Element " + it) is an error)
if you want to change the implementation later and return some subclass instance instead, it will get even more confusing that HashSet(n) { "Element $it" } will construct not a HashSet but, for example, a LinkedHashSet
It's better to show it explicitly that it's a factory function, not a constructor, to avoid this confusion.
Naming a function same to a class is generally avoided in stdlib, too. Given SomeClass, in stdlib a preferred naming style for factory functions is someClassOf, someClassBy or whatever explains the semantics of the function best. The examples:
generateSequence { ... } and sequenceOf(...)
lazy { ... } and lazyOf(...)
compareBy { ... }
listOf(...), setOf(...), mapOf(...)
So, one should definitely have strong reason to have a function mimic a constructor.
Instead, a function's name might tell a user more (even everything) about its usage.
I agree with +hotkey. It's probably best to avoid confusion in this case.
If it's only used internally and all the other devs (if any) are okay with it, though, I'd say to go for it. Python acknowledges that idea and I love it. Heck, they go both ways, being okay with you naming a class in function case, too, if it feels more like it's acting like a function. But, Python doesn't have to deal with Java interop, so definitely don't do it for public code.
Is it possible to add a new static method to the java.lang.Math class in Kotlin? Usually, such things are possible in Kotlin thanks to Kotlin Extensions.
I already tried doing the following in a file I made called Extensions.kt:
fun Math.Companion.clamp(value:Double,minValue:Double,maxValue:Double):Double
{
return Math.max(Math.min(value,maxValue),minValue)
}
but Math.Companion could not be resolved...
As of Kotlin 1.3, this is not possible. However, it's being considered for a future release!
To help this feature get implemented, go vote on this issue: https://youtrack.jetbrains.com/issue/KT-11968
Because all proposals are basically in limbo right now, I wouldn't hold my breath that this will get in any time soon
I think this is not possible. Documentation says the following:
If a class has a companion object defined, you can also define extension functions and properties for the companion object.
The Math class is a Java class, not a Kotlin one and does not have a companion object in it. You can add a clamp method to the Double class instead.
As of Kotlin 1.2 it is still not possible.
As a workaround, to statically "extend" Environment class I am currently using:
Class EnvironmentExtensions {
companion object {
#JvmStatic
fun getSomething(): File {
...
return Environment.something()
}
}
}
It is not an ideal solution but IntelliJ/Android Studio code completion helps with the usage:
val something = EnvironmentExtensions.getSomething()