You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by Eric Berry <el...@gmail.com> on 2019/06/12 22:21:58 UTC

I think I've found an issue with the @MapConstructor defaulting implementation, when used with @NamedDelegate.

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
@Immutable.

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
like:

> 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.
turtlepaws.com - When was the last time you changed your master password?
Blog: http://eric-berry.blogspot.com
jEdit <http://www.jedit.org> - Programmer's Text Editor