acejazz acejazz - 1 month ago 8
Groovy Question

Why is it not possible to access "dependsOn" and "description" task properties in the same way?

By reading the Gradle documentation I see that both

description
and
dependsOn
are property for the
Task
class.
We can set the
description
property in different ways:

task MyTask
MyTask.description("This is MyTask")
MyTask.description "This is MyTask"
MyTask.description = "This is MyTask"


But even though
dependsOn
is a property as well, we can only have:

task MyTask
task AnotherTask
MyTask.dependsOn(AnotherTask)
MyTask.dependsOn AnotherTask


Because it is not possible to use:

MyTask.dependsOn = AnotherTask


The error is:


Cannot cast object 'task ':AnotherTask'' with class
'org.gradle.api.DefaultTask_Decorated' to class 'java.lang.Iterable'


I can't understand what kind of cast is it done behind the scene.
Is it a cast involving only the
=
operator for a property?

Answer Source

You can still use = operator to "assign" a value to dependsOn property, but you have to provide a valid type:

MyTask.dependsOn = [AnotherTask]

Otherwise you get ClassCastException:

> Cannot cast object 'task ':AnotherTask'' with class 'org.gradle.api.DefaultTask_Decorated' to class 'java.lang.Iterable'

Groovy myTask.x = ... vs. calling myTask.setX(...) method

And this is correct behavior - if you take a look inside org.gradle.api.Task interface source code you will find that description is not a field but a pair of getter and setter methods:

@Internal
String getDescription()

void setDescription(String var1);

and the same applies to dependsOn "property":

@Internal
Set<Object> getDependsOn()

void setDependsOn(Iterable<?> var1)

In this case Groovy allows you to access both methods like they were properties but it passes execution to those methods. So when you do:

MyTask.description

you actually call MyTask.getDescription(). And when you do:

MyTask.description = 'This is description'

you actually call MyTask.setDescription('This is description')

MyTask.dependsOn() case

There is also one thing worth mentioning about dependsOn case. As I mentioned earlier org.gradle.api.Task interface contains Set<Object> getDependsOn() and void setDependsOn(Iterable<?> var1). But that's not all. There is also a method:

Task dependsOn(Object... var1);

It expects varargs (single argument of type Object or array of Object[]). In this case you can execute following method calls:

MyTask.dependsOn AnotherTask // -> equivalent of MyTask.dependsOn(AnotherTask)
MyTask.dependsOn([AnotherTask] as Object[]) 

but you cannot call:

MyTask.dependsOn [AnotherTask]
MyTask.dependsOn [AnotherTask] as Object[] // -> equivalent of (MyTask.dependsOn [AnotherTask]) as Object[]

First case wont work, because you need to explicitly cast a collection to Obejct[] array. Second one also wont work, because casting to Object[] has to be applied to a parameter. Skipping parentheses in this case causes that compiler tries to apply casting to a result of the method and we end up with ClassCastException from the first case.

I hope it helps.