groovy-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Eric Berry <>
Subject I think I've found an issue with the @MapConstructor defaulting implementation, when used with @NamedDelegate.
Date Wed, 12 Jun 2019 22:21:58 GMT
I have a static method marked with @NamedVariant, that takes a
"Configuration" object as a parameter, which is marked with @NamedDelegate.
The "Configuration" object is defined as a static inner class marked with

My Class:

> class CsvRowMapper {
>     private final Settings settings
>     @NamedVariant
>     CsvRowMapper(Settings settings) {
>         this.settings = settings
>     }
> *@NamedVariant    static CsvRowMapper parse(@NamedDelegate Settings
> settings, Reader reader) {        new CsvRowMapper(settings).parse(reader)
>   }*
>     CsvRowMapper parse(Reader source) {
>         // do work here
>         return this
>     }
> *@Immutable    static class Settings {        String separator = ','
>   boolean headers = true        int headersRow = 0        int firstDataRow
> = 1    }*
> }

I've highlighted the important parts. The issue is when I use this class

> CsvRowMapper.parse(*separator: '\t'*, tsvFileReader)

I get an `GroovyCastException` because it's trying to set `headersRow` or
`firstDataRow` to `null`:

org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast
> object '{separator= , headers=null, headersRow=null, firstDataRow=null}'
> with class 'java.util.LinkedHashMap' to class 'CsvRowMapper$Settings' due
> to: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot
> cast object 'null' with class 'null' to class 'int'. Try
> 'java.lang.Integer' instead

I loaded up this into GroovyConsole, and I think the issue is that the
`@NameVariant` version of the `parse` method is creating a map with all
keys for the `@NamedDelegate` `Settings` class (See NamedVariant.png).

Furthermore the `@Immutable` annotation adds `@MapConstructor` to my
`Settings` class, which only checks if the given map contains the *`key`*,
not also if it has a `value` (See MapConstructor.png).

I'm thinking this might have been on purpose, to allow for null values to
override defaults? Though I can't think of a situation where I would want
that to be the case.

Maybe an elvis operator when a default is provided would be better?

> this .separator = args.get('separator')' ?: ,'

Thank you for your time and consideration.
Eric Berry

Learn from the past. Live in the present. Work towards the future. - When was the last time you changed your master password?
jEdit <> - Programmer's Text Editor

View raw message