IceTeaGreen
IceTeaGreen

Reputation: 153

In my implementation I want to have different return type of the method

I have the following problem. I have the interface:

public interface Parser {
    public Map<String, List<String>> parse() throws IOException;
}

I have two implementations:

public class RacerInfoParser implements Parser{

    private final Path path;

public RacerInfoParser(Path path) {
    this.path = path;   
}

@Override
public Map <String, List<String>> parse() throws IOException {
    try (Stream<String>lines = Files.lines(path)){
        
        Map <Object, Object> map = lines.collect(Collectors.toMap(
                string -> string.substring(0,3),
                string -> Arrays.asList(string.substring(4).split("_"))));
        
        Map<String, List<String>> result = new HashMap<>((Map) map);
        return result;
        }
    }
}

and

public class TimeParser implements Parser {

    private final Path path;

public TimeParser(Path path) {
    this.path = path;   
}


@Override
public Map <String, List<String>> parse() throws IOException {
    try (Stream<String>lines = Files.lines(path)){
        
        Map <Object, Object> map = lines.collect(Collectors.toMap(
                string -> string.substring(0,3),
                string -> Arrays.asList(string.substring(3).split("_"))));
        Map<String, List<String>> result = new HashMap<>((Map) map);
    return result;
        }
    }
}

What I want to do is to change the code and the return type of TimeParser so that it returns the result of type Map<String, List <LocalTime>. I have read that in order to have a different type I need to have a sub-class of the parent type, but I don't understand how to do it in my case.

P.S. I know that Map<String, List<String>> result = new HashMap<>((Map) map); is a bad code, but I don't know yet how to properly convert Map<Object, Object to Map<String, List<String>>. If you have any suggestions I will be glad to listen to them:).

P.S.S. I use these two implementations because I beleive they do the same thing: parse text from log and txt files:

    public class RacerBuilder {
        public List<Racer> buildRacers () throws URISyntaxException, IOException {
    
            Parser racerInfoParser = new RacerInfoParser(Paths.get(getClass().getClassLoader()
                      .getResource("abbreviations.txt").toURI()));
            Parser startTimeParser = new TimeParser(Paths.get(getClass().getClassLoader()
                      .getResource("start.log").toURI()));
            Parser endTimeParser = new TimeParser(Paths.get(getClass().getClassLoader()
                      .getResource("end.log").toURI()));
            
            Map<String, List<String>> racerInfoMap = racerInfoParser.parse();
            Map<String, List<String>> startTimeMap = startTimeParser.parse();
            Map<String, List<String>> endTimeMap = endTimeParser.parse();
    
            return racerInfoMap.keySet().stream()
                    .map(i -> new Racer (i,
                            racerInfoMap.get(i).get(0),
                            racerInfoMap.get(i).get(1),
                            startTimeMap.get(i).get(1),
                            endTimeMap.get(i).get(1),
                            endTimeMap.get(i).get(0)))
                    .collect(Collectors.toList());  
           }
}

Racer class now has several fields, all of them are Strings. I want it to have 2 fields of type LocalTime.

Upvotes: 0

Views: 76

Answers (3)

Anirudh Jadhav
Anirudh Jadhav

Reputation: 1007

You can use generics T. Like this way

interface Parser<T> {
    public Map<String, List<T>> parse() throws IOException;
}

class RacerInfoParser implements Parser<String>{

    private final Path path;

    public RacerInfoParser(Path path) {
        this.path = path;   
    }
    
    @Override
    public Map <String, List<String>> parse() throws IOException {
        try (Stream<String>lines = Files.lines(path)){
            
            Map <String,  List<String>> map = lines.collect(Collectors.toMap(
                    string -> string.substring(0,3),
                    string -> Arrays.asList(string.substring(4).split("_"))));
            return map;
            }
        }
    }

class TimeParser implements Parser<LocalTime> {

    private final Path path;

public TimeParser(Path path) {
    this.path = path;   
}


@Override
public Map <String, List<LocalTime>> parse() throws IOException {
    try (Stream<String>lines = Files.lines(path)){
        
        Map<String, List<LocalTime>> result = lines.collect(Collectors.toMap(
                string -> string.substring(0,3),
                string -> Arrays.stream(string.substring(4).split("_"))
                .map(LocalTime::parse)
                .collect(Collectors.toList())
                ));
            return result;
        }
    }
}

Upvotes: 0

Youcef LAIDANI
Youcef LAIDANI

Reputation: 60046

To accept either :

Map<String, List<String>>

or

Map<String, List<LocalTime>>

You can use generic in this case, all you need is to use :

<T> Map<String, List<T>> parse() throws IOException;
                     ^

Also your code can be :

return lines.collect(Collectors.toMap(
        string -> string.substring(0, 3),
        string -> Arrays.asList(string.substring(4).split("_"))));

Or if you want a List of LocalTime, you can parse your String and collect as this:

return lines.collect(Collectors.toMap(
        string -> string.substring(0, 3),
        string -> Arrays.stream(string.substring(4).split("_"))
        .map(LocalTime::parse) // or you can use a Date time formatter
        .collect(Collectors.toList())

You don't need to cast with this solution.

Upvotes: 3

Rob Evans
Rob Evans

Reputation: 2874

I'd wrap the Map<String, List<String>> in a new class with a getter, let's call it MapAsString. Make it part of a class hierarchy so you have class MapAsString extends DataMap. Next have a new class that is a subclass of DataMap called perhaps MapAsLocalTime where MapAsLocalTime extends DataMap.

Bonus points: make your parent class DataMap abstract and provide a single abstract method you must implement. that uses Generics to return a List<String, T>. You can have a constructor that takes a T (generic type) which defines what type T will be at construction time. If this seems too hard, perhaps just have it return anything using the wildcard ?... so getter returns List<String, ?> - here ? can an object of any type

Upvotes: 2

Related Questions