0

I am trying to make a system that will allow me to take all the items in one array, and, with a factory object, convert them into another type of item, and fill a second array with those new objects, and return that. I want to do this all with generics so this can be done in a single-line static method call. However, I'm getting an error that I can't quite parse. Here is (I hope) all the relevant code:

Problem class has:

// ...

// "ArrayPP" is my take on ArrayList that has a lot of convenience methods.
// The constructor here uses varargs to initialize the array:
ArrayPP<String> myStrings = new ArrayPP<>("foo", "bar", "baz");
// MyCharSequenceImpl is just a class that implements CharSequence.
ArrayPP<MyCharSequenceImpl> myCSI = new ArrayPP<>();
// The error occurs on the following line:
myCSI = ArrayPP.transfer(myStrings, new CSToMyCSIFactoryDelegate());

// ...

ArrayPP has:

public class ArrayPP<T> implements Iterable<T> {
    // ...
    // Iterable implementations
    // ...

    public static <From, To> ArrayPP<To> transfer(Iterable<From> origin, FactoryDelegate<From, To> factory)
    {
        ArrayPP<To> ret = new ArrayPP<>();
        for (From f : origin) {
            ret.add(factory.makeFromFactory(f));
        }
        return ret;
    }

    // ...
}

FactoryDelegate has:

public interface FactoryDelegate<From, To> extends Factory<To> {
    public To makeFromFactory(From basis);
}

Factory has:

public interface Factory<To> {
    public To makeFromFactory();
}

CSToMyCSIFactoryDelegate has:

public class CSToMyCSIFactoryDelegate implements FactoryDelegate<CharSequence, MyCharSequenceImpl> {
    @Override
    public MyCharSequenceImpl makeFromFactory(CharSequence basis) {
        // MyCharSequenceImpl has a constructor that converts any CharSequence
        // into a new MyCharSequenceImpl.
        return new MyCharSequenceImpl(basis);
    }

    @Override
    public MyCharSequenceImpl makeFromFactory() {
        return makeFromFactory("");
    }
}

Here is the error I'm getting:

error: method transfer in class ArrayPP<T> cannot be applied to given types;
        myCSI = ArrayPP.transfer(myStrings, new CSToMyCSIFactoryDelegate());
                       ^
  required: Iterable<From>,FactoryDelegate<From,To>
  found: ArrayPP<String>,TransferTest.CSToMyCSIFactoryDelegate
  reason: inferred type does not conform to equality constraint(s)
    inferred: CharSequence
    equality constraints(s): CharSequence,String
  where From,To,T are type-variables:
    From extends Object declared in method <From,To>transfer(Iterable<From>,FactoryDelegate<From,To>)
    To extends Object declared in method <From,To>transfer(Iterable<From>,FactoryDelegate<From,To>)
    T extends Object declared in class ArrayPP

The most confusing part of this error, to me, is that it says inferred type does not conform to equality constraint(s), and then that the inferred type is CharSequence and the equality constraints are CharSequence,String...

Why am I getting this error, and how can I fix my code?

Ky -
  • 30,724
  • 51
  • 192
  • 308
  • Someone correct me if I'm wrong, but isn't the inferred type only happening because of this line: `ArrayPP myStrings = new ArrayPP<>("foo", "bar", "baz");`? If you change it to `new ArrayPP`, then you wouldn't be inferring anything and your code should compile, no? – NoseKnowsAll Mar 02 '15 at 18:01
  • @NoseKnowsAll no, the diamond operator is a feature of Java 7 that simply removes redundancy when the declaration and creation use the same type. By the time it gets to the problem line, the compiler already knows that `myStrings` is an `ArrayPP`. – Ky - Mar 02 '15 at 18:04
  • 1
    awesome. Thanks for clearing that up to me. – NoseKnowsAll Mar 02 '15 at 18:08
  • While all this is OK, I wonder (if I may ask), why you are writing this code. If it's for learning purposes, then good for you! However, [Google Guava library](https://code.google.com/p/guava-libraries/wiki/FunctionalExplained) offers this functionality (and much more), and performs all the transformations *lazily*. I highly recommend it. – fps Mar 02 '15 at 18:27
  • @Magnamag I prefer to minimize external dependencies. Adding a single method to a class I've long used in my code (I've used `ArrayPP` in some form since early 2011), and then utilizing interfaces that I've also already made (`Factory` and `FactoryDelegate` are part of the same repo as `ArrayPP`), is not only faster for me but more reliable because I know if/when I will ever end support for my own code (as of now, these classes are indefinitely supported). – Ky - Mar 02 '15 at 18:39
  • 1
    @Supuhstar I kindly disagree :) There's no point in reinventing the wheel, especially if the older wheel is more robust, better tested, used worldwide and even more beautiful. Anyway, looking at that library's code might shed some light on the use of generics. BTW, you could also accomplish what you want by using Java 8 streaming API and lambdas. Good luck! – fps Mar 02 '15 at 18:44
  • 1
    @Magnamag to each their own! There are upsides and downsides to using external libraries versus making your own functionality. I personally just don't want to juggle 27 JARs when I'm only making a simple desktop utility. ;3 – Ky - Mar 02 '15 at 19:02

2 Answers2

3

It appears that what you need is

public static <From, To> ArrayPP<To> transfer(
    //       vvvvvvvvv
    Iterable<? extends From> origin,
    FactoryDelegate<From, To> factory
)

Because you only care that origin produces From when you iterate over it, not that it is necessarily an Iterable<From>.

See also

Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • now I get this error: `error: non-static variable this cannot be referenced from a static context` `myCSI = ArrayPP.transfer(myStrings, new CSToMyCSIFactoryDelegate());` and it points to the `n` in `new` – Ky - Mar 02 '15 at 18:06
  • Sounds like maybe `CSToMyCSIFactoryDelegate` is an inner class. You'd need to post the complete code for us to tell for sure why you get that error. – Radiodef Mar 02 '15 at 18:08
  • @Radiosef oh man you're right! I forgot to make them static in my one-file test, but forgot to check and see that the fix does indeed work in my production code. Thanks! – Ky - Mar 02 '15 at 18:22
2

From has to be a String from first argument, and has to be a CharSequence from the second. The fact that String is a CharSequence does not matter for the generics the way you describe them. Try Iterable<? extends From> may be?