This message was deleted.
# plugin-development
s
This message was deleted.
t
How are you supposed to wire dependencies if you no longer expose `Provider`s? e.g. `tasks.someTask { someProperty.set(someExtension.value) }`: if
value
is a
Provider
then it can be (re)configured after the wiring, otherwise you're forced to write
someProperty.set(provider { someExtension.value })
. And in case
value
is actually computed by another task, you also get an implicit dependency on that task that you lose with
provider {}
. …for the very small gain of writing
value = "foo"
rather than
value.set("foo")
.
b
The focus here is concise end-use api and easy fallback property chaining. For library authors the syntax is more verbose to achieve that, but in a nutshell, you'd never use delegated access internally when applying your plugin logic and instead would use actual properties with internal visibility. So you'd have something like
tasks.someTask { someProperty.set(someExtension._value) }
where _value is of type Property with internal visibility
Obviously this does not work if your consumer wants to make use of this for local custom tasks. It's not a silver bullet that fits all kind of plugins.
f
I like the project, and I understand where you are coming from with this. But, I'm with @Thomas Broyer here and also believe that Gradle should not add this API. It's just another way to do the same thing with reduced functionality and additional reflection overhead. However, nothing wrong with a little project that can be used by those who cannot stand the
.set(...)
instead of
= ...
.
b
@Fleshgrinder where does reflection come in all this? I thought delegates are just syntactic sugar handled by the compiler?
f
Just look at the signature of any standard
getValue
function and you have your answer.
Copy code
operator fun getValue(thisRef: Any, property: KProperty<*>)
Notice the
KProperty<*>
which originates from, well, reflection.
b
I kinda assumed it to be shallow reflection like MyType::class
Might be wrong tho
a
KPropery<*>
is one of the reflection primitives found in the stdlib and not the reflection. That being said, the
getValue
operator function does not rely on reflection at all
b
Aha! I knew I didn't dream it up 😀
f
https://kotlinlang.org/docs/delegated-properties.html#translation-rules-for-delegated-properties it's even stated in the docs with illustration of the generated code. Also directly go to the next heading after the one I linked, this was added recently and doesn't need reflection. Reflection primitive or full reflection almost never matters, it's still Java reflection underneath.
and
this::prop
is a reflection object of the
KProperty
type describing prop itself.
b
Following your logic, does that mean passing method references instead of lambdas as arguments also use reflection??
Copy code
public void sayHi(Predicate<String> filter) {}
public Boolean myPredicate(String value) {}
public static void main() {
  sayHi(::myPredicate)
  // VS
  sayHi((value) -> true)
}
t
I'd expect the Kotlin compiler to do the same kind of code generation as the Java compiler on that one, and not use reflection. That said, KProperty implementations could probably also be generated at compile-time and not use reflection, I have no idea what the Kotlin compiler actually does (maybe have a look at your
build/classes/…
dir and possibly use
javap
to find out)
f
Function references are different, they are not going to be handed over to another function that expects a
KFunction
(or anything like that). What it generates also depends on what
kotlinc
flags you have used. By default it generates a
kotlin.jvm.functions.FunctionXXX
(where
XXX
is the artity, so e.g.
0
for no args,
1
for one arg, up to
22
). However, you can pass
-Xlambdas=indy
(JVM 8+) to use
invocedynamic
instead. (There are also
-Xsam-conversions=indy
and
-Xstring-concat=indy
[JVM 9+].) This is the reason why it's important to use
inline
whenever you take a lambda. If you do that the
FunctionXXX
does not need to be created. However, something many people are often not looking out for is the the code that is going to be inlined should be minimal, or it disables the optimization functionalities of HotSpot.
Given:
Copy code
package com.hellofresh

import java.util.function.Predicate

class C {
    fun sayHi(filter: Predicate<String>) = Unit
    fun myPredicate(value: String): Boolean = true
}

fun main() {
    val c = C()
    c.sayHi(c::myPredicate)
}
The Kotlin Bytecode viewer of IntelliJ gives:
Copy code
// C.java
package com.hellofresh;

import java.util.function.Predicate;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 5, 1},
   k = 1,
   d1 = {"\u0000$\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000b\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u000e\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u0006J\u0014\u0010\u0007\u001a\u00020\b2\f\u0010\t\u001a\b\u0012\u0004\u0012\u00020\u00060\n¨\u0006\u000b"},
   d2 = {"Lcom/hellofresh/C;", "", "()V", "myPredicate", "", "value", "", "sayHi", "", "filter", "Ljava/util/function/Predicate;", "kotlin-libs.logging.main"}
)
public final class C {
   public final void sayHi(@NotNull Predicate filter) {
      Intrinsics.checkNotNullParameter(filter, "filter");
   }

   public final boolean myPredicate(@NotNull String value) {
      Intrinsics.checkNotNullParameter(value, "value");
      return true;
   }
}
// TestKt.java
package com.hellofresh;

import java.util.function.Predicate;
import kotlin.Metadata;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 5, 1},
   k = 2,
   d1 = {"\u0000\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001¨\u0006\u0002"},
   d2 = {"main", "", "kotlin-libs.logging.main"}
)
public final class TestKt {
   public static final void main() {
      C c = new C();
      Function1 var1 = (Function1)(new Function1(c) {
         // $FF: synthetic method
         // $FF: bridge method
         public Object invoke(Object var1) {
            return this.invoke((String)var1);
         }

         public final boolean invoke(@NotNull String p1) {
            Intrinsics.checkNotNullParameter(p1, "p1");
            return ((C)this.receiver).myPredicate(p1);
         }
      });
      c.sayHi((Predicate)(new Predicate(var1) {
         // $FF: synthetic field
         private final Function1 function;

         {
            this.function = var1;
         }

         // $FF: synthetic method
         public final boolean test(Object t) {
            Object var10000 = this.function.invoke(t);
            Intrinsics.checkNotNullExpressionValue(var10000, "invoke(...)");
            return (Boolean)var10000;
         }
      }));
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}
As you can see, here it even creates two objects under the hood.
b
I specifically gave my example in java to understand how java handles such cases
I know kotlin has lots of tricks up its sleeve to help avoid lambdas, but I used lambas as an example for java day-to-day reflection in the first place 😄
f
Java introduced lambdas with 8 and uses said
invokedynamic
. Kotlin does not because Kotlin has back comp down to 6.
b
Got it, thanks
😊 1