我该如何解决泛型实例化问题?

时间:2018-09-20 17:26:13

标签: java parsing generics

我正在为3个文件(例如“ city_id”;“ country_id”;“ region_id”;“ name”)制作CSV解析器,并且遇到了通用的实例化问题。有什么办法可以解决并坚持使用DRY吗? (我看到答案可能是在构造函数中使用T,但我看不到如何在我的情况下正确使用它。)

compiling Fortran sources
Fortran f77 compiler: C:\msys64\mingw64\bin\gfortran.exe -Wall -g -ffixed-form -fno-second-underscore -O3 -funroll-loops
Fortran f90 compiler: C:\msys64\mingw64\bin\gfortran.exe -fopenmp -O3 -funroll-loops
Fortran fix compiler: C:\msys64\mingw64\bin\gfortran.exe -Wall -g -ffixed-form -fno-second-underscore -fopenmp -O3 -funroll-loops
compile options: '-Ic:\users\ben\appdata\local\temp\tmpzsrysh\src.win-amd64-2.7 -IC:\Users\Ben\Anaconda2\lib\site-packages\numpy\core\include -IC:\Users\Ben\Anaconda2\include -IC:\Users\Ben\Anaconda2\PC -c'
gfortran.exe:f90: zernsurf.f95
gfortran.exe:f77: c:\users\ben\appdata\local\temp\tmpzsrysh\src.win-amd64-2.7\zernsurf-f2pywrappers.f
could not find library 'gomp' in directories []
C:\msys64\mingw64\bin\gfortran.exe -Wall -g -Wall -g -shared ..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o ..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\users\ben\appdata\local\temp\tmpzsrysh\src.win-amd64-2.7\zernsurf-f2pywrappers.o -LC:\msys64\mingw64\lib\gcc\x86_64-w64-mingw32\7.3.0 -LC:\Users\Ben\Anaconda2\libs -LC:\Users\Ben\Anaconda2\PCbuild\amd64 -LC:\Users\Ben\Anaconda2\PC\VS9.0\amd64 -o c:\users\ben\appdata\local\temp\tmpzsrysh\Release\extra-dll\libzernsurf.NVNMLU4BAM43JLHGAZSG5DZQSFKWQ4FF.gfortran-win_amd64.dll -Wl,--allow-multiple-definition -Wl,--output-def,c:\users\ben\appdata\local\temp\tmpzsrysh\Release\libzernsurf.NVNMLU4BAM43JLHGAZSG5DZQSFKWQ4FF.gfortran-win_amd64.def -Wl,--export-all-symbols -Wl,--enable-auto-import -static -mlong-double-64
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0xb55): undefined reference to `omp_get_num_threads'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0xb62): undefined reference to `omp_get_thread_num'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0x1080): undefined reference to `omp_get_num_threads'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0x108d): undefined reference to `omp_get_thread_num'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0x15ae): undefined reference to `omp_get_num_threads'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0x15ba): undefined reference to `omp_get_thread_num'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0x5cd0): undefined reference to `GOMP_parallel'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0x5e09): undefined reference to `GOMP_parallel'
..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o:zernsurf.f95:(.text+0x659c): undefined reference to `GOMP_parallel'
collect2.exe: error: ld returned 1 exit status
error: Command "C:\msys64\mingw64\bin\gfortran.exe -Wall -g -Wall -g -shared ..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\zernsurf.o ..\..\users\ben\appdata\local\temp\tmpzsrysh\Release\users\ben\appdata\local\temp\tmpzsrysh\src.win-amd64-2.7\zernsurf-f2pywrappers.o -LC:\msys64\mingw64\lib\gcc\x86_64-w64-mingw32\7.3.0 -LC:\Users\Ben\Anaconda2\libs -LC:\Users\Ben\Anaconda2\PCbuild\amd64 -LC:\Users\Ben\Anaconda2\PC\VS9.0\amd64 -o c:\users\ben\appdata\local\temp\tmpzsrysh\Release\extra-dll\libzernsurf.NVNMLU4BAM43JLHGAZSG5DZQSFKWQ4FF.gfortran-win_amd64.dll -Wl,--allow-multiple-definition -Wl,--output-def,c:\users\ben\appdata\local\temp\tmpzsrysh\Release\libzernsurf.NVNMLU4BAM43JLHGAZSG5DZQSFKWQ4FF.gfortran-win_amd64.def -Wl,--export-all-symbols -Wl,--enable-auto-import -static -mlong-double-64" failed with exit status 1

2 个答案:

答案 0 :(得分:1)

这是我使用Java 8可以想到的DRYest版本。

public static <T> List<T> parseCsvFile(String filePath, Function<String[], T> mapper) {
    return Files.lines(new File(filePath).toPath())
                .map(s -> s.replace("\"", "").split(";"))
                .map(mapper)
                .collect(Collectors.toList());
}

像这样使用。

List<Foo> foos = parseCsvFile("foos.csv", columns -> {
    return new Foo(columns[0], columns[1], columns[2], columns[3]);
});

答案 1 :(得分:1)

为什么不起作用

在Java中行不通的是通用给定类的new T(...)实例化。您只能将new关键字与特定的类名一起使用。

在运行时,您的csvParcer()方法甚至都不知道T使用了哪个类,对于JVM,T将被Object替换。因此,您的方法无法知道要实例化哪个类。您需要在方法中传递一些信息,以允许您针对给定情况实例化所需的类。

反射解决方案

一种方法是在您的方法中添加一个参数,以命名您要实例化的类:

public static <T> List<T> csvParcer(String filePath, Class<T> tClazz) {
    List<T> cities = new ArrayList<>();
    String line;
    String[] dividedLine;
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
        reader.readLine();
        while ((line = reader.readLine()) != null) {
            dividedLine = line.replace("\"", "").replace(";", " ").split(" ");

            Constructor<T> myConstructor = tClazz.getConstructor(String.class, String.class, String.class);
            T object = myConstructor.newInstance(dividedLine[0], dividedLine[1], dividedLine[2], dividedLine[3]);

            cities.add(object);
        }
        return cities;
    } catch (Exception ex) {
        throw new RuntimeException("Error reading " + filePath, ex);
    }
}

[顺便说一句,如果我的方法无法正确读取和解析文件,我更改了错误处理以引发异常,因为这是告诉调用者他无法获得结果的首选方法。] < / p>

缺点是您浪费了运行时性能(与读取CSV文本文件相比并不明显),并且如果您需要的类没有一个完全接受四个字符串的公共构造函数,则不会出现编译时错误

工厂对象的解决方案

这是Leo已经提出的方法,您传入一个封装实例创建的对象-一个“工厂”对象,并且对于要从CVS阅读器获取的每个不同的T类,都需要一个对象。 Leo使用优雅的Java-8流编码样式重写了您的示例,但是在经典样式中也可以实现,更接近您的原始想法。首先,我们需要一个工厂接口:

public interface TFactory<T> {
    T create(String arg0, String arg1, String arg2, String arg3);
}

解析器方法如下:

public static <T> List<T> csvParcer(String filePath, TFactory<T> factory) {
    List<T> cities = new ArrayList<>();
    String line;
    String[] dividedLine;
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
        reader.readLine();
        while ((line = reader.readLine()) != null) {
            dividedLine = line.replace("\"", "").replace(";", " ").split(" ");

            T object = factory.create(dividedLine[0], dividedLine[1], dividedLine[2], dividedLine[3]);

            cities.add(object);
        }
        return cities;
    } catch (Exception ex) {
        throw new RuntimeException("Error reading " + filePath, ex);
    }
}

您可以像以下示例一样使用它:

private void example() {
    TFactory<City> cityFactory = new TFactory<City>() {
        @Override
        public City create(String arg0, String arg1, String arg2, String arg3) {
            return new City(arg0, arg1, arg2, arg3);
        }
    };
    List<City> cities = csvParcer("C:\\temp\\cities.csv", cityFactory);
} 

具有四个显式的String参数使代码比使用String []数组更为冗长,但是为您提供了额外的编译时安全性。