Generyki w Javie: odczytanie kolumn z CSV dla double i String

0

Taka sytuacja:
Mam 2 klasy odpowiedzialne za wczytywanie słowników z csv.
1 klasa pobiera 2 kolumny z csv i zapisuje jako Map<String, String>.
2 klasa pobiera 2 kolumny z csv i zapisuje jako Map<String, Double>.

Co zrobić by nie dublować kodu? Ano zrobić klasę bardziej ogólną odpowiedzialną za tworzenie Map z csv. No to robię:

package Main;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.function.Function;

public interface MapFromCsv<T1, T2> {

	public default Map<T1, T2> getMap(String FILE_TO_LOAD){
		Map<T1, T2> mapResult = new HashMap<>();
		File f = new File(FILE_TO_LOAD);
		try (Scanner sc = new Scanner(f)) {
			while (sc.hasNextLine()) {
				String tmp = sc.nextLine();
				String[] tmp2 = tmp.split(";");
				mapResult.put((T1) tmp2[0], (T2) tmp2[1]);
			}
		} catch (FileNotFoundException fntf) {
			System.err.println("File " + f.getPath() + "was not found.");
		} catch (Exception e) {
			System.err.println("Something went wrong with reading " + f.getPath());
		}
		return mapResult;
	}
}

klasa1:

package Main;

import java.util.Map;

public enum AreaMapFromCsv implements MapFromCsv<String, String> {

	INSTANCE;
	final String FILE_TO_LOAD = "resources/areaMap.csv";
	private Map<String, String> areaMap;

	private AreaMapFromCsv() {
		initAreaMap();
	}

	private void initAreaMap() {
		this.areaMap = getMap(FILE_TO_LOAD);
	}

	public void reloadAreaMap() {
		initAreaMap();
	}

	public Map<String, String> getAreaMap() {
		return areaMap;
	}

}

klasa2:

package Main;

import java.util.Map;

public enum CurrencyMapFromCsv implements MapFromCsv<String, Double>{

	INSTANCE;
	final String FILE_TO_LOAD = "resources/currencyMap.csv";
	private Map<String, Double> currencyMap;

	private CurrencyMapFromCsv() {
		initCurrency();
	}

	private void initCurrency() {
		this.currencyMap = getMap(FILE_TO_LOAD);
	}

	public void reloadCurrency() {
		initCurrency();
	}

	public Map<String, Double> getCurrencyMap() {
		return currencyMap;
	}

}

Tylko teraz jest problem taki o, że klasa MapToCsv nie wie jak zamienić Stringa na doubla i wywala się na castowaniu. Postanowiłem mu wlepić funkcje, ale mi nie działa:

package Main;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.function.Function;

public interface MapFromCsv<T1, T2> {

	public default Map<T1, T2> getMap(String FILE_TO_LOAD, Function<String, T1> foo1, Function<String, T2> foo2){
		Map<T1, T2> mapResult = new HashMap<>();
		File f = new File(FILE_TO_LOAD);
		try (Scanner sc = new Scanner(f)) {
			while (sc.hasNextLine()) {
				String tmp = sc.nextLine();
				String[] tmp2 = tmp.split(";");
				mapResult.put((T1) foo1.apply(tmp2[0]), (T2) foo2.apply(tmp2[1]));
			}
		} catch (FileNotFoundException fntf) {
			System.err.println("File " + f.getPath() + "was not found.");
		} catch (Exception e) {
			System.err.println("Something went wrong with reading " + f.getPath());
		}
		return mapResult;
	}
}

i wtedy bym w klasie1 przekazał jako 2 funkcje: String::toString, a w klasie2: String::toString i Double::parseDouble.

EDIT: jednak działa. :O Jak to bardziej przejrzyście zrobić?

1

Zrób sobie w interfejsie metodę, która będzie konwertowała wczytane dane na odpowiedni typ generyczny i nadpisz ją w klasach implementujących interfejs.

0
Aterwik napisał(a):

Zrób sobie w interfejsie metodę, która będzie konwertowała wczytane dane na odpowiedni typ generyczny i nadpisz ją w klasach implementujących interfejs.

czyli tak lepiej?

//...
@Override
convertMap Map<String, Double> (Map<String, String> mp){
    Map<String, Double> mp2 = new HashSet<>();
    mp.entrySet().stream().forEach(x -> mp2.put(x.getKey(), Double.parseDouble(x.getValue())));	
    return mp2;
}
//...
1

Raczej chodziło mi mniej więcej o coś takiego. Sensowniej tu chyba będzie też zrobić klasę abstrakcyjną zamiast interfejsu.

public abstract class MapLoader<T1, T2> {

    public  Map<T1, T2> getMap(String FILE_TO_LOAD){
        Map<T1, T2> mapResult = new HashMap<>();
        File f = new File(FILE_TO_LOAD);
        try (Scanner sc = new Scanner(f)) {
            while (sc.hasNextLine()) {
                String tmp = sc.nextLine();
                String[] tmp2 = tmp.split(";");
                mapResult.put((T1) tmp2[0], interpretData(tmp2[1]));
            }
        } catch (FileNotFoundException fntf) {
            System.err.println("File " + f.getPath() + "was not found.");
        } catch (Exception e) {
            System.err.println("Something went wrong with reading " + f.getPath());
        }
        return mapResult;
    }

    protected abstract T2 interpretData(String data); // tutaj konwersja
}

0

A da się tak w Javie zrobić by nadpisywanie tej abstrakcyjnej metody było opcjonalne? Ewentualnie przekazywać ją opcjonalnie w argumentach metody jako Function ?

Chodzi o to, że większość moich słowników nie będzie wymagać tutaj konwersji, bo będzie <String, String> dla par <key, value>, więc nie chce mi się za każdym razem nadpisywać tej metody (s) -> s.

1 użytkowników online, w tym zalogowanych: 0, gości: 1