简体   繁体   English

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

[英]JAVA: Cast String to (dynamically known) primitive type in order to instantiate a (dynamically known) class

I have a repository class that uses text files ( a requirement ), meaning that I have to read strings and cast them in order to instantiate objects. 我有一个使用文本文件一个要求 )的存储库类,这意味着我必须读取字符串并转换它们才能实例化对象。 The problem is that I want my repository class as general as I can make it, in order to use it to manipulate different object types. 问题是我希望我的存储库类可以像我一样使用它,以便使用它来操作不同的对象类型。

So, is there a (more elegant) way to dynamically cast strings to whatever field (primitive) type it needs at runtime, while avoiding lots of try-catch structures with numerous ifs/switches? 那么,是否有一种(更优雅的)方法可以将字符串动态地转换为运行时所需的任何字段(原始)类型,同时避免使用大量ifs / switch的try-catch结构?

As a short simplified version, I want objectA.txt to contain only objectA's information, similarly for objectB.txt , and my Repository code to handle both: 作为一个简短的简化版本,我希望objectA.txt只包含objectA的信息,类似于objectB.txt和我的Repository代码来处理两者:

Repository repo A = new Repository("objectA.txt", < list of Types for A >); 存储库repo A =新存储库(“objectA.txt”,<A> 的类型列表 ); Type A a=repoA.getOne(); 输入A a = repoA.getOne();

Repository repo B = new Repository("objectB.txt", < list of Types for B >); 存储库repo B =新存储库(“objectB.txt”,< B类型列表 >); Type B b=repoB.getOne(); B b = repoB.getOne();

What I have: 是)我有的:

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();
        }
    }

} }

PS: I'm a JAVA newbie, so please keep the explanations as simple as possible. PS:我是JAVA新手,所以请尽可能简单地解释。

No IDE on hand, so I hope this makes sense: 没有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);
}

Then when you need to create the parameters for your constructor: 然后,当您需要为构造函数创建参数时:

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

I think you can make use of auto-boxing and auto-unboxing coupled with the observation that all wrapper classes provide a method named valueOf that accepts a String and returns an instance of the respective (wrapper) type such that the given string represents the legal value of that type. 我认为你可以利用自动装箱和自动拆箱加上观察所有包装类提供一个名为valueOf的方法,该方法接受一个String并返回相应(包装)类型的实例,使得给定的字符串代表合法的该类型的价值

The following is an attempt of a type-safe implementation that suits your needs: 以下是针对您的需求的类型安全实现的尝试:

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");
    }
}

If you want to identify the type of primitive data type from a String, you can use the following: 如果要从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");
            }
        }

    }
}

Output: 输出:

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

The above code categorizes your String in 4 major parts long integer, double, boolean and if none matches then String. 上面的代码将您的String分为4个主要部分long integer,double,boolean,如果none则匹配String。 As java states, primitive data types fall in two categories: 正如java所述,原始数据类型分为两类:

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

This way you will be able to identify the types in which your String lies. 这样,您就可以识别String所在的类型。 You can modify the code to check the range and type cast the numbers accordingly in byte and short as well. 您可以修改代码以检查范围并键入相应的数字,也可以是字节和短。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM