2

I have the following Code which is a boiled-down version of something I've stumbled upon:

public class Transforming
{
    static interface MyInterface<T>
    {
        void consume(T... toConsume);
    }

    static abstract class Mapper<T> implements MyInterface<String> {
        MyInterface<T> delegate;

        public Mapper(MyInterface<T> delegateTo)
        {
            delegate = delegateTo;
        }

        public void consume(String... transformFrom)
        {
            T[] array = (T[]) Arrays.stream(transformFrom)
                    .map(this::transform)
                    .toArray(); // can't toArray(T[]::new) here!
            delegate.consume(array);
        }

        protected abstract T transform(String toTransform);
    }
}

The searches on how to transform streams to arrays fall short obviously since I don't have the resulting type of array at this point, and Java doesn't allow me to create arrays of a generic type...

I do understand the issue, but any input on how to clean code this? AFAICT, my options here are

  • change the interface from varargs to List
  • the cast I'm using in the code sample
  • adding an IntFunction to the Mapper creation

Anything I'm missing? What would be your preference?

2
  • Your Mapper.consume method doesn't implement interface because it have String instead of T in parameter type. Commented Jul 17, 2017 at 11:07
  • @talex You are correct of course, I corrected the OP to implement MyInterface<String>. Commented Jul 17, 2017 at 11:13

2 Answers 2

5

The way I handle this is by always providing two overloads:

  • One which accepts varargs

  • One which accepts a List<>.

The varargs overload never does anything other than pack the array into a list and invoke the List<> overload. This keeps things simple. No-brainer.

So, essentially, the option I'd choose is your first option, "change the interface from varargs to List", except that you do not actually have to change it, you can just extend it by adding an overload.

Sign up to request clarification or add additional context in comments.

3 Comments

This is actually what I had before :) I'm trying to get rid of the List-version of the method in the interface because it's not called anywhere.
"Not called anywhere" or "only called from one place" is not a reason to remove a method from an interface. The criteria for deciding whether a method belongs to an interface or not depend exclusively upon the functionality that the interface needs to provide, not on the use that callers may (or may not) do. The optional function here is the varargs function, which is being provided purely for the convenience of the callers. The fundamental function is the one that accepts a list.
Exactly. Since the varargs method could be provided as default method in the interface, there is no reason to ever think about removing a method (unless the varargs method turns out to be useless). For the implementor, it makes no difference whether the single method to implement is consume(T...) or consume(List<T>)
0

Your abstract Mapper class could use an abstract toArray method which provide the typed conversion from list to array.

static abstract class Mapper<T> implements MyInterface<String> {

    @Override
    public void consume(String... transformFrom) {
        T[] array = toArray(Arrays.stream(transformFrom)
                .map(this::transform)
                .collect(Collectors.toList()));
        delegate.consume(array);
    }

    protected abstract T transform(String toTransform);
    protected abstract T[] toArray(List<T> list);
}

In implementations just implement a basic list.toArray(..) method

public static void main(String[] args) {
    Mapper myMap = new Mapper<Integer>(new MapperInt()) {

        @Override
        protected Integer transform(String toTransform) {
            return new Integer(toTransform);
        }

        @Override
        protected Integer[] toArray(List<Integer> list) {               
            return list.toArray(new Integer[list.size()]);
        }

    };
    myMap.consume("1","2");
}

public static class MapperInt implements MyInterface<Integer> {

    @Override
    public void consume(Integer... toConsume) {
        for(Integer i: toConsume)
            System.err.println(i);
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.