Reputation: 153
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
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
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
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