JAVA:将String转换为(动态已知的)基本类型,以实例化(动态已知)类

时间:2016-03-18 16:28:44

标签: java reflection

我有一个使用文本文件需求)的存储库类,这意味着我必须读取字符串并将其转换为实例化对象。问题在于我希望我的存储库类能够像我一样使用它,以便使用它来操作不同的对象类型。

那么,是否有一种(更优雅的)方法可以将字符串动态地转换为运行时所需的任何字段(基本)类型,同时避免大量的 try-catch结构有多个ifs / switch?

作为一个简短的简化版本,我希望 objectA.txt 只包含objectA的信息,类似于 objectB.txt ,以及我要处理的Repository代码两个:

存储库repo A =新存储库(" objectA.txt",< A的类型列表>);输入 A a = repoA.getOne();

存储库repo B =新存储库(" objectB.txt",< B的类型列表>);输入 B b = repoB.getOne();

我有什么:

public class FileRepository extends InMemoryRepository{
    private String fileName;
    private List<Class> types;

    public FileRepository(String fileName, List<Class> types) {
        //@param types 
        //     - list containing the Class to be instantiated followed by it's field types
        super();
        this.fileName = fileName;
        this.types=types;
        loadData();
    }

    private void loadData() {
        Path path = Paths.get(fileName);

        try {
            Files.lines(path).forEach(line -> {
                List<String> items = Arrays.asList(line.split(","));

                //create Class array for calling the correct constructor
                Class[] cls=new Class[types.size()-1];
                for (int i=1; i<types.size(); i++){
                    cls[i-1]=types.get(i);
                }

                Constructor constr=null;
                try {
                    //get the needed constructor
                    constr = types.get(0).getConstructor(cls);
                } catch (NoSuchMethodException e) {
                    //do something
                    e.printStackTrace();
                }
                //here is where the fun begins
                //@arg0 ... @argn are the primitives that need to be casted from string 
                //something like: 
                //*(type.get(1))* arg0=*(cast to types.get(1))* items.get(0);
                //*(type.get(2))* arg1=*(cast to types.get(2))* items.get(1);
                //...

                Object obj= (Object) constr.newInstance(@arg0 ... @argn);

            });
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

}

P.S。:我是JAVA的新手,所以请尽量保持解释。

3 个答案:

答案 0 :(得分:2)

手头没有IDE,所以我希望这是有道理的:

private static final Map<Class, Function<String, ?>> parsers = new HashMap<>();

static {
  parsers.put(Long.class, Long::parseLong);
  parsers.put(Integer.class, Integer::parseInt);
  parsers.put(String.class, String::toString);
  parsers.put(Double.class, Double::parseDouble);
  parsers.put(Float.class, Float::parseFloat);
  // add your own types here.
}

public <T> T parse(Class<T> klass, String value) {
  // add some null-handling logic here? and empty values.
  return (T)parsers.get(klass).apply(value);
}

然后,当您需要为构造函数创建参数时:

parameters =
     IntStream
     .range(0, cls.size-1)
     .map(i -> (Object)parse(types.get(i), items.get(i)))
     .toArray(Object[]::new);

答案 1 :(得分:0)

我认为你可以利用自动装箱和自动拆箱加上观察所有包装类提供一个名为valueOf的方法,该方法接受 String 并返回一个实例相应的(包装)类型,使得给定的字符串代表该类型的合法

以下是针对您的需求的类型安全实现的尝试:

import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.Consumer;

/**
 * Created by kmhaswade on 3/18/16.
 */
//@ThreadUnsafe
public class NonStreamingGenericPrimitiveDataRepo<T> implements Iterable<T> {

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return theIterator.hasNext();
            }

            @Override
            public T next() {
                String next = theIterator.next();
                try {
                    Method m = theType.getDeclaredMethod("valueOf", String.class);
                    return (T) m.invoke(null, next);
                } catch (NoSuchMethodException | IllegalAccessException e) {
                    throw new RuntimeException("This is impossible!");
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("data: " + next + " does not represent type: " + theType);
                }
            }
        };
    }

    @Override
    public void forEach(Consumer<? super T> action) {
        throw new RuntimeException("left as an exercise :-) ");
    }
    private final ArrayList<String> theCache;
    private final Iterator<String> theIterator;
    private final Class<T> theType;
    public NonStreamingGenericPrimitiveDataRepo(Reader reader, Class<T> theType) throws IOException {
        Objects.requireNonNull(reader);
        Objects.requireNonNull(theType);
        if (Integer.class.equals(theType)
                || Long.class.equals(theType)
                || Float.class.equals(theType)
                || Double.class.equals(theType)
                || Boolean.class.equals(theType)
                || String.class.equals(theType)) {
            theCache = new ArrayList<>();
            try (BufferedReader br = new BufferedReader(reader)) {
                String line;
                while ((line = br.readLine()) != null)
                    theCache.add(line);
            }
            theIterator = theCache.iterator();
            this.theType = theType;
        } else {
            throw new IllegalArgumentException("Not a wrapper type: " + theType);
        }
    }

    public static void main(String[] args) throws IOException {
        for (int i : new NonStreamingGenericPrimitiveDataRepo<>(ints(), Integer.class))
            System.out.println("read an int: " + i);
        for (float f : new NonStreamingGenericPrimitiveDataRepo<>(floats(), Float.class))
            System.out.println("read a float: " + f);
        for (boolean b: new NonStreamingGenericPrimitiveDataRepo<>(booleans(), Boolean.class))
            System.out.println("read a boolean: " + b);
    }
    static StringReader ints() {
        return new StringReader("1.w\n2\n-3\n4\n");
    }
    static StringReader floats() {
        return new StringReader("1.0f\n3.25f\n-3.33f\n4.44f\n");
    }
    static StringReader booleans() {
        return new StringReader("false \ntrue\n");
    }
}

答案 2 :(得分:0)

如果要从String中识别原始数据类型的类型,可以使用以下命令:

public class Test {

    final static String LONG_PATTERN = "[-+]?\\d+";
    final static String DOUBLE_PATTERN = "[-+]?(\\d*[.])?\\d+";
    final static String BOOLEAN_PATTERN = "(true|false)";
    final static String CHAR_PATTERN = "[abcdefghijklmnopqrstuvwxyz]";

    public static void main(String[] args) {

        String[] xList= {
                "1", //int 
                "111111111111", //long 
                "1.1", //float
                "1111111111111111111111111111111111111111111111111111.1", //double
                "c", //char
                "true", //boolean
                "end" //String
                };

        for (String x: xList){

            if(x.matches(LONG_PATTERN)){
                long temp = Long.parseLong(x);
                if (temp >= Integer.MIN_VALUE && temp <= Integer.MAX_VALUE){
                    System.out.println( x + " is int use downcast");
                } else {
                    System.out.println( x + " is long");
                }
            } else if(x.matches(DOUBLE_PATTERN)){
                double temp = Double.parseDouble(x);
                if (temp >= Float.MIN_VALUE && temp <= Float.MAX_VALUE){
                    System.out.println( x + " is float use downcast");
                } else {
                    System.out.println( x + " is Double");
                }
            } else if (x.toLowerCase().matches(BOOLEAN_PATTERN)){
                boolean temp = x.toLowerCase().equals("true");
                System.out.println(x + " is Boolean");
            } else if(x.length() == 1){
                System.out.println(x + " is char");
            }else {
                System.out.println( x + " is String");
            }
        }

    }
}

输出:

1 is int use downcast
111111111111 is long
1.1 is float use downcast
1111111111111111111111111111111111111111111111111111.1 is Double
c is char
true is Boolean
end is String

上面的代码将您的String分为4个主要部分long integer,double,boolean,如果none则匹配String。正如java所述,原始数据类型分为两类:

Integers
    byte
    char (represented as a character)
    short
    int
    long
Floating point numbers
    float
    double
Boolean
    boolean

通过这种方式,您将能够识别String所在的类型。您可以修改代码以检查范围并键入相应的数字,以字节和短的形式。