1

I see that as of Groovy 2.0 there is the possibility to add a TypeChecked annotation to classes or methods to trigger the optional static checking.

I must admit that I am confused by how such a thing could work. The article gives simple examples such as

@TypeChecked
Date test() {
    // compilation error:
    // cannot assign value of Date 
    // to variable of type int
    int object = new Date()

    String[] letters = ['a', 'b', 'c']
    // compilation error:
    // cannot assign value of type String 
    // to variable of type Date
    Date aDateVariable = letters[0]

    // compilation error:
    // cannot return value of type String 
    // on method returning type Date
    return "today"
}

In this case it is clear that some checks will fail. But in the general case, one will use, say, the return value of method A, that is not type checked, inside the type checked method B. In this case I do not see how the compiler can figure out whether method B types are consistent, since it does not have enough information on the return value of method A.

How can one enable type checking on a subset of the code without losing type safety in general?

EDIT

I try to make an example. What if I have an old class

class Old {
  public getFoo() {
    return 1
  }
}

and try to use it from type-checked code, like

@TypeChecked
class New {
  int doubleFoo() {
    return 2 * (new Old().foo)
  }
}

The compiler just does not know what to do, and I guess it will fail to compile (I do not have Groovy2 installed here at work to check). But if this is the case, it become an issue to use any code written before Groovy2. So I imagine something more sophisticated is done, but I am not sure what.

Andrea
  • 20,253
  • 23
  • 114
  • 183

1 Answers1

1

There is no such problem. If you call a method from an unchecked class, then it's the declared return type which is used and the behaviour is exactly the same as if you use a Java method from Groovy.

Take the following example:

class A {
   int foo() { 1 }
}

@TypeChecked
class B {
   void bar() {
       int x = new A().foo() // uses the type from A.foo, correct
   }
}

Now, imagine this:

class A {
   Date foo() { new Date() }
}

@TypeChecked
class B {
   void bar() {
       int x = new A().foo() // uses the type from A.foo, foo returns a Date, the compiler throws an error
   }
}

Note that in general, you use mixed type checking in a single class if you have code which heavily relies on dynamic code that the type checker obviously cannot check. This is the case, for example, if you rely on a builder. If so, then you just declare a method which uses the builder as not checked, and the rest of your code is checked. As long as the unchecked method returns a compatible type, type safety is guaranteed.

melix
  • 1,510
  • 7
  • 6
  • The fact is, a typical groovy class which is not designed to be typechecked will have something like `def foo() { 1 }`. AFAIK, this should be equivalent to returning `Object`. Now, when you try, for instance, to feed this result into a typechecked method which expects an `int` (or `Integer`, after boxing), the type checker will complain. – Andrea Sep 18 '12 at 10:01
  • Is it possible that my experience with Groovy is not representative, but I have found lots of code which does not explicitly declare return types and simply uses `def`. I have even found this advised as best practice, since in any case declaring a return type in Groovy 1 would not give a type check, but only a cast at runtime, resulting in code that is no more safe, possibly hides a bug due to casting and it is slower. – Andrea Sep 18 '12 at 10:03
  • A dynamic language will usually be a bit more slower; groovy is a FAST dynamic language, so you need to check your case: does maximum performance is needed? I worked on a web application for 2 years using groovy, and mostly of our problems came from high JSF 1.2 memory usage and bad mapping on JPA. If you need maximum performance, `@CompileStatic`. If needing type checking, i think you are right that you should type your methods' signatures, else you can go for `def`. In your case, i think the compiler is not inferring the return type of the method. Maybe filling a JIRA? – Will Sep 18 '12 at 12:40
  • I did not mean that the problem is slowness, maybe I have expressed myself badly. But the fact is that adding types for return values in Groovy 1 does not have performance beneftis (if anything it is the opposite), does not add type safety (it casts at runtime) and may hide bugs. Because of this, the code I have usually seen does not contain type hints on return values. This is just an aside. The actual question is: *how can the compiler type check stuff when many methods return values of unknown types*? It is not a rant, I am really trying to figure out. – Andrea Sep 18 '12 at 16:07
  • I think that doesn't work in groovy. The compiler will not complain about, neither infer, return values. That's part of being a dynamic language ;-). When using `@CompileStatic`, the compiler also won't infer the type of your return when your method contains a `def`, you are better using the explicit return type. When using groovy, your code should be covered with tests. A system i worked didn't had any tests, so we always used typing in our methods. – Will Sep 24 '12 at 11:50