groovy

Objects are sometimes converted on assignment

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Critical Critical
  • Resolution: Won't Fix
  • Affects Version/s: 1.5.4
  • Fix Version/s: 1.5.5
  • Component/s: None
  • Labels:
    None
  • Number of attachments :
    0

Description

String x = "123".length()

works while it should throw a ClassCastException. I don't see why there should be default behavior in Groovy that converts objects to make an assignment work. If the object type is wrong the assignment shall - by default - always fail with ClassCastException.

Activity

Hide
John Wilson added a comment -

It also does silent narrowing coercions (double to int for example). I don't think a strongly typed language should do this either.

int i = 2.5d should fail either at compile time or at run time

char c = 'd'
int i = c should work OK of course

Show
John Wilson added a comment - It also does silent narrowing coercions (double to int for example). I don't think a strongly typed language should do this either. int i = 2.5d should fail either at compile time or at run time char c = 'd' int i = c should work OK of course
Hide
Guillaume Laforge added a comment -

And also the list litterals to arrays should still work too.

int[] someInts = [1,2,3,4]

Show
Guillaume Laforge added a comment - And also the list litterals to arrays should still work too. int[] someInts = [1,2,3,4]
Hide
Jim White added a comment -

This is not a bug. This is the intended behavior for Groovy and is not new (check Groovy 1.0 for example). If this were changed in any way most any non-trivial (and a lot of trivial) Groovy applications would break utterly.

http://groovy.codehaus.org/JN3015-Types

Groovy is not nearly as strongly typed as Java and that is by design and is a major source of it's brevity.

I don't think there is a way to prevent coercion to a String. For example:

groovy -e 'class Foo { String toString() { "hey" } }; String x= new Foo(); println ([x, x.getClass()])'

If you want to make sure something is a String before you assign it to a String variable then use an assertion.

Show
Jim White added a comment - This is not a bug. This is the intended behavior for Groovy and is not new (check Groovy 1.0 for example). If this were changed in any way most any non-trivial (and a lot of trivial) Groovy applications would break utterly. http://groovy.codehaus.org/JN3015-Types Groovy is not nearly as strongly typed as Java and that is by design and is a major source of it's brevity. I don't think there is a way to prevent coercion to a String. For example: groovy -e 'class Foo { String toString() { "hey" } }; String x= new Foo(); println ([x, x.getClass()])' If you want to make sure something is a String before you assign it to a String variable then use an assertion.
Hide
Russel Winder added a comment -

I do not see this as a bug but a feature. If I write:

def s = '123'.length ( )

then s is whatever type the expression delivers. If I write:

String s = '123'.length ( )

then I am saying that s must refer to a String. I find it natural that the result of the expression is coerced to the type I have demanded. I would expect a ClassCastException only if there is no coercion possible.

Show
Russel Winder added a comment - I do not see this as a bug but a feature. If I write:
def s = '123'.length ( )
then s is whatever type the expression delivers. If I write:
String s = '123'.length ( )
then I am saying that s must refer to a String. I find it natural that the result of the expression is coerced to the type I have demanded. I would expect a ClassCastException only if there is no coercion possible.
Hide
Jim White added a comment -

Not only is the behavior consistent, it is done to support extremely common cases which are so common folks don't realize they're using it.

String s = "$something and something else"

The object on the RHS is groovy.lang.GString, not java.lang.String (which is final and therefore has no subclasses). Just because it has quotes around it doesn't mean squat when it comes to strong typing.

Also it is the coercion (calling the Object.toString() method) that causes the GString to build its output and can be don't multiple times with different results.

Ditto for passing a non-string to a j.l.String paramter. This particular example is even trickier because there are two one parameter constructors for java.io.File, but Groovy is very cool and finds the one that is assignable from the GString (which URI is not):

def f = new File("${somewhere}/Foo")

So now we've seen that both java.lang.Number and groovy.lang.GString are coerced to java.lang.String routinely. These are the common cases but the mechanism is much more general purpose that that.

Show
Jim White added a comment - Not only is the behavior consistent, it is done to support extremely common cases which are so common folks don't realize they're using it.
String s = "$something and something else"
The object on the RHS is groovy.lang.GString, not java.lang.String (which is final and therefore has no subclasses). Just because it has quotes around it doesn't mean squat when it comes to strong typing. Also it is the coercion (calling the Object.toString() method) that causes the GString to build its output and can be don't multiple times with different results. Ditto for passing a non-string to a j.l.String paramter. This particular example is even trickier because there are two one parameter constructors for java.io.File, but Groovy is very cool and finds the one that is assignable from the GString (which URI is not):
def f = new File("${somewhere}/Foo")
So now we've seen that both java.lang.Number and groovy.lang.GString are coerced to java.lang.String routinely. These are the common cases but the mechanism is much more general purpose that that.
Hide
Jim White added a comment -

I neglected to include the Number case.

Page 177 of GinA:

"You saw in chapter 3 that Groovy provides autoboxing and coercion when it makes sense.".

It then shows this example Listing 7.2 (top of 178):

final static String PI = 3.14
assert PI.class.name == 'java.lang.String'

Notice how a BigDecimal was coerced to j.l.String. He then shows that you cannot assign j.l.String to a Float.

The bug here is a lack of a proper language specification for Groovy, not what Groovy actually is doing.

Also note that the output I showed from the little test case above is Groovy 1.0.

Show
Jim White added a comment - I neglected to include the Number case. Page 177 of GinA:
"You saw in chapter 3 that Groovy provides autoboxing and coercion when it makes sense.".
It then shows this example Listing 7.2 (top of 178):
final static String PI = 3.14
assert PI.class.name == 'java.lang.String'
Notice how a BigDecimal was coerced to j.l.String. He then shows that you cannot assign j.l.String to a Float. The bug here is a lack of a proper language specification for Groovy, not what Groovy actually is doing. Also note that the output I showed from the little test case above is Groovy 1.0.

People

Vote (0)
Watch (2)

Dates

  • Created:
    Updated:
    Resolved: