0

I have some Java code like the following snippet (the example is simplified from the original code). A yellow squiggle and warning message appear, as indicated by the code comment below. The message is: "This method invocation is unsafe since the passed arguments may be of a wrong type."

abstract class Seek<T> {
    abstract <S> Seek<S> seek(S... o);
    abstract <T2> void map(Func<T,T2> call);
    interface Func<I,O> {
        public O call(I x);
    }
    public <X2> void go(Func<T,X2> c, T some) {
        seek(some).map(c); // <- yellow squiggle here on 'c'
    }
}

Why does the warning appear? What is the best way to fix this?

Note: I'm using the AIDE development environment for Android.

EDIT: I fixed an error in the code after reading the answer from @tsolakp and @LouisWasserman.

intrepidis
  • 2,870
  • 1
  • 34
  • 36
  • 1
    I would have thought this has something to do with the generic array creation on `seek(S...)`. Can you make `seek` take a `List` instead? – Andy Turner Jul 10 '17 at 19:56
  • 1
    Based on your method signatures, `T`, `T2` and `X2` are all superfluous. – shmosel Jul 10 '17 at 19:57
  • 1
    The warning message from AIDE is misleading. When I compile your code with the command-line compiler (version 1.8.0_112), I don't get a warning for `c` on the line you indicated. Instead, I get a warning on the declaration of `seek` about "Possible heap pollution from parameterized vararg type S" and a second warning in the call to `seek` about "unchecked generic array creation for varargs parameter of type X1[]". – Ted Hopp Jul 10 '17 at 20:04
  • @TedHopp, thanks I thought maybe it's something like that. AIDE can be a little /loose/ with its warning explanations. – intrepidis Jul 10 '17 at 20:11

4 Answers4

1

map should only have T2 as a type parameter, not T. Right now the T shadows the T from the Seek<T> class, that is, you have two type variables named T that are actually different, and you don't want that.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • Thanks I didn't understand this at first, but you're correct. I've updated the question, however this change doesn't fix the warning... of course. – intrepidis Jul 10 '17 at 20:26
1

Actually there are three warnings in that code:

  1. Type safety: Potential heap pollution via varargs parameter o at abstract <S> Seek<S> seek(S... o);

  2. The type parameter T is hiding the type T at abstract <T,T2> void map(Func<T,T2> call);

  3. Type safety: A generic array of X1 is created for a varargs parameter at seek(some).map(c);

It can be clean of warnings A. take out varargs parameter from generic like:

abstract class Seek<T> {
   abstract <S> Seek<S> seek(S o); // <- no more yellow squiggle
   abstract <T1,T2> void map(Func<T1,T2> call); // <- no more yellow squiggle
   interface Func<I,O> {
      public O call(I x);
   }
   public <X1,X2> void go(Func<X1,X2> c, X1 some) {
      seek(some).map(c); // <- no more yellow squiggle here on 'c'
   }
 }

B. define arrays explicitly like:

  abstract class Seek<T> {
  abstract <S> Seek<S> seek(S[] o);        // <- no more yellow squiggle
  abstract <T2> void map(Func<T,T2> call); // <- no more yellow squiggle
  interface Func<I,O> {
    public O call(I x);
  }
  public <X1,X2> void go(Func<X1,X2> c, X1[] some) {
    seek(some).map(c); // <- no more yellow squiggle
  }
}

but, S[] o is quite not the same as S... o. It can take only Array explicitly. Maybe you need to reconsider your design?

IMHO: I really do not understand needs to have that many Generic type parameters at the same time on class and methods level...

Vadim
  • 4,027
  • 2
  • 10
  • 26
  • I tried this and it worked! Thanks! I didn't realise `varargs` was so different to passing a solid array. – intrepidis Jul 10 '17 at 20:50
  • BTW, the real code is much bigger than that given, I just wanted to reduce it down while still keeping the original warning. – intrepidis Jul 10 '17 at 20:52
  • Understood... but usually I use Generic for the class (i.e. `Seek`) when number of implementations has to deal with different actual classes. While on the method level (i.e. `public void go(Func c, X1 some)`) is more likely for utility kind of methods when containing class may not be even abstract or generic... But it is matter of taste... :-) – Vadim Jul 10 '17 at 20:59
  • Actually most of the `generics` complexity above comes from using the TotallyLazy functional library. Take a look at its `Sequences.sequence(T...)` method. – intrepidis Jul 10 '17 at 21:28
0

The warnings I get from compiling your code are related to the generic array creation from the seek(S...) method.

If you can change this to be a List<S>, you can invoke it using Arrays.asList():

abstract class Seek<T> {
    abstract <S> Seek<S> seek(List<S> o);

    // ...

    public <X1,X2> void go(Func<X1,X2> c, X1 some) {
        seek(Arrays.asList(some)).map(c);
    }
}
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

Change T to T1 in the map method. You will also get generic array warning too. Here is modified version with no warnings:

abstract static class Seek<T> {

    abstract <S> Seek<S> seek(List<S> s);

    abstract <T1,T2> void map(Func<T1, T2> call);

    interface Func<I,O> {
        public O call(I x);
    }

    public <X1,X2> void go(Func<X1,X2> c, X1 some) {
        seek( Arrays.asList(some) ).map(c); 
    }
}

After some comments it appers that OP might have intended to make map method to be bound with Seek class generic type. If thats the case we can use this solution, as suggested by @Louis Wasserman and @Andy Turner:

abstract static class Seek<T> {

    abstract <S> Seek<S> seek(List<S> s);

    abstract <T2> void map(Func<T, T2> call);

    interface Func<I,O> {
        public O call(I x);
    }

    public <X1,X2> void go(Func<X1,X2> c, X1 some) {
        seek( Arrays.asList(some) ).map(c); 
    }
}
tsolakp
  • 5,858
  • 1
  • 22
  • 28
  • Changing `T` to `T1` has no effect. It's just replacing one arbitrary name with another. – Andy Turner Jul 10 '17 at 20:08
  • Wrong. His `map` takes `Func` that has two generic parameters. Therefore it needs to either declare two generic parameters in `map` signature or just use `Seek`'s generic type. Since OP did not mention what is expected I assume map needs not use `Seek`'s generic type. – tsolakp Jul 10 '17 at 20:14
  • Yep, but declaring `` on the method declares a new type variable `T` which shadows the one on the class. – Andy Turner Jul 10 '17 at 20:15
  • Thats why it needs to be change to something else like `T1`. Since second parameter is `T2` I assume the `map` method does not need to be bound by class level `T`. – tsolakp Jul 10 '17 at 20:16
  • Changing `T` to `T1` doesn't actually change the warnings produced by the compiler. – Andy Turner Jul 10 '17 at 20:18
  • It does. The next warning is about generic varargs. – tsolakp Jul 10 '17 at 20:20
  • Thanks for updating your answer. I have tried your suggestion but now get a similar warning for `Arrays.asList(some)`. It says "Passing this argument is unsafe because an instance of type 'java.util.List' may not be of type 'java.util.List'." – intrepidis Jul 10 '17 at 20:40