#java
#java
Вопрос:
У меня следующая проблема. У меня есть интерфейс:
public interface Parser {
public Map<String, List<String>> parse() throws IOException;
}
У меня есть две реализации:
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 resu<
}
}
}
и
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 resu<
}
}
}
Что я хочу сделать, так это изменить код и возвращаемый тип анализатора времени, чтобы он возвращал результат типа Map<String, List <LocalTime>
. Я читал, что для того, чтобы иметь другой тип, мне нужно иметь подкласс родительского типа, но я не понимаю, как это сделать в моем случае.
PS Я знаю, что Map<String, List<String>> result = new HashMap<>((Map) map);
это плохой код, но я пока не знаю, как правильно преобразовать Map<Object, Object
в Map<String, List<String>>
. Если у вас есть какие-либо предложения, я буду рад их выслушать :).
PSS Я использую эти две реализации, потому что я верю, что они делают одно и то же: анализируют текст из файлов журнала и txt:
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 теперь имеет несколько полей, все они являются строками. Я хочу, чтобы в нем было 2 поля типа LocalTime.
Комментарии:
1. Сделайте ваш анализатор универсальным интерфейсом:
public interface Parser<T> { ... }
с помощью методаMap<String, List<T>> parse()
.2. Также.
Map <String, List<String>>
выглядит сложным для возвращаемого типа. Возможно, вам не хватает понимания домена. СамMap <String, List<T>>
по себе может быть новым классом.3. Не могли бы вы, пожалуйста, подумать / добавить в код вопроса, который показывает, как вы планируете использовать тот факт, что эти два класса реализуют один и тот же интерфейс
Parser
. В чем преимущество членства в одной иерархии при разных типах возвращаемых данных?4. @MarkBramnik, я немного отредактировал свой вопрос, теперь я надеюсь, что это немного понятнее.
5. @IceTeaGreen что вы подразумеваете под последней строкой, и давайте закроем вопрос, если вы удовлетворены ответом, который вы приняли.
Ответ №1:
Чтобы принять либо :
Map<String, List<String>>
или
Map<String, List<LocalTime>>
В этом случае вы можете использовать generic, все, что вам нужно, это использовать :
<T> Map<String, List<T>> parse() throws IOException;
^
Также ваш код может быть :
return lines.collect(Collectors.toMap(
string -> string.substring(0, 3),
string -> Arrays.asList(string.substring(4).split("_"))));
Или, если вам нужен список LocalTime, вы можете проанализировать свою строку и собрать ее следующим образом:
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())
Вам не нужно использовать это решение.
Ответ №2:
Я бы обернул Map<String, List<String>>
в новый класс с помощью getter, назовем его MapAsString
. Сделайте его частью иерархии классов, чтобы у вас было class MapAsString extends DataMap
. Затем создайте новый класс, который является подклассом DataMap
called , возможно MapAsLocalTime
, where MapAsLocalTime extends DataMap
.
Бонусные баллы: сделайте свой родительский class DataMap
абстрактный и предоставьте один абстрактный метод, который вы должны реализовать. который использует дженерики для возврата a List<String, T>
. У вас может быть конструктор, который принимает T
(общий тип), который определяет, какой тип T
будет во время построения. Если это кажется слишком сложным, возможно, просто верните что-нибудь, используя подстановочный ?
знак… итак, возвращает getter List<String, ?>
— здесь ?
может быть объект любого типа
Ответ №3:
Вы можете использовать generics T. Например, таким образом
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 resu<
}
}
}