Search code examples
javagenericsjava-7type-conversionjava-5

Create a setter to provide a java List Implementation class


I'm writing a small type-conversion framework, based on this interface:

public interface Converter <From,To> {
    public To convert(final From from);
}

So that I can write custom type converters such as

public class BigDecimal2Double implements Converter<BigDecimal, Double> {

    @Override
    public Double convert(final BigDecimal value) {
        return value != null ? value.doubleValue() : null;
    }
}

The following class is intended to extend the capability of a converter, to deal with Lists of data..

public abstract class ListConverter<From,To> implements Converter<From,To> {

    public List<To> convertList(List<From> fromList){

        if (fromList == null) {return null;}
        List<To> toList = new ArrayList<>(); // DEFAULT IMPLEMENTATION

        for(From from: fromList ){
            toList.add(this.convert(from));
        }

        return toList;
    }

    public static <From,To> ListConverter<From, To> extend(final Converter<From, To> converter){
        return new ListConverter<From, To>() {

            @Override
            public To convert(From from) {
                return converter.convert(from);
            }
        };
    }
}

Thus I can write client code such as:

ListConverter<BigDecimal,Double> lcb2d = ListConverter.extend(new BigDecimal2Double());
List<BigDecimal> bdList = retrieveSomeBigDecList();
List<Double> convertedList = lcb2d.convertList(bdList);

As you can see this can only provide a default ArrayList implementation for the converted list.

I would like to add the capability to provide a different List implementation from the client code, e.g:

ListConverter<BigDecimal,Double> lcb2d = ListConverter.extend(new BigDecimal2Double());
List<BigDecimal> bdList = retrieveSomeBigDecList();
lcbd.setToListImplementation(LinkedList.class); // How to define such a method?
List<Double> convertedList = lcb2d.convertList(bdList); // would be a LinkedList

How could I accomplish that? The following code inside ListConverter class doesn't compile:

private Class<? extends List<To>> toListImplementation = ArrayList.class;
public void setToListImplementation(Class<? extends List<To>> listImpl){
    toListImplementation = listImpl;
}

Also how should I replace this line, using the toListImpl private field?

//... OLD
List<To> toList = new ArrayList<>(); // DEFAULT IMPLEMENTATION
//... NEW 
List<To> toList = toListImplementation.newInstance(); // WOULD WORK???

Edit: Java 8 is very cool, I know there are great features. I still work on java 7 though and I would like to accomplish java 5 compliance.


Solution

  • Provide a way for a user of class ListConverter to supply a factory method to create the specific kind of List that is needed. Example:

    import java.util.ArrayList;
    import java.util.List;
    import java.util.function.Supplier;
    
    public abstract class ListConverter<From, To> implements Converter<From, To> {
    
        private final Supplier<List<To>> listFactory;
    
        public ListConverter(Supplier<List<To>> listFactory) {
            this.listFactory = listFactory;
        }
    
        public List<To> convertList(List<From> fromList) {
            if (fromList == null) {
                return null;
            }
    
            List<To> toList = listFactory.get();
            for (From from : fromList) {
                toList.add(this.convert(from));
            }
    
            return toList;
        }
    
        public static <From, To> ListConverter<From, To> extend(Converter<From, To> converter, Supplier<List<To>> listFactory) {
            return new ListConverter<From, To>(listFactory) {
    
                @Override
                public To convert(From from) {
                    return converter.convert(from);
                }
            };
        }
    
        public static <From, To> ListConverter<From, To> extend(Converter<From, To> converter) {
            return extend(converter, ArrayList::new);
        }
    }
    

    If you would for example want a LinkedList then you could do:

    ListConverter<BigDecimal,Double> lcb2d =
            ListConverter.extend(new BigDecimal2Double(), LinkedList::new);
    

    edit - For Java 5-7, you can create a ListFactory interface:

    import java.util.List;
    
    public interface ListFactory<T> {
        List<T> newList();
    }
    

    And then use that, and use anonymous inner classes instead of Java 8 method references:

    import java.util.ArrayList;
    import java.util.List;
    
    public abstract class ListConverter<From, To> implements Converter<From, To> {
    
        private final ListFactory<To> listFactory;
    
        public ListConverter(ListFactory<To> listFactory) {
            this.listFactory = listFactory;
        }
    
        public List<To> convertList(List<From> fromList) {
            if (fromList == null) {
                return null;
            }
    
            List<To> toList = listFactory.newList();
            for (From from : fromList) {
                toList.add(this.convert(from));
            }
    
            return toList;
        }
    
        public static <From, To> ListConverter<From, To> extend(Converter<From, To> converter, ListFactory<To> listFactory) {
            return new ListConverter<From, To>(listFactory) {
                @Override
                public To convert(From from) {
                    return converter.convert(from);
                }
            };
        }
    
        public static <From, To> ListConverter<From, To> extend(Converter<From, To> converter) {
            return extend(converter, new ListFactory<To>() {
                @Override
                public List<To> newList() {
                    return new ArrayList<To>();
                }
            });
        }
    }