質問

The problem is: after initializing VTDGen, VTDNav or AutoPilot in constructor, in the other method they refer to null and the appropriate exception is thrown.

public class Configuration {
public Configuration(String dir, String filename, ResourceBundle resourceBundle) throws IOException, IndexWriteException, IndexReadException {
    String XMLFilename = filename + ".xml";
    String indexFilename = filename + ".vxl";
    vtdGen = new VTDGen();

    vtdGen.parseFile("D:/Librarian/config/configuration.xml", true);
    vtdNav = vtdGen.getNav();
    autoPilot = new AutoPilot(vtdNav);
    boolean gen = vtdGen!=null;
    boolean nav =  vtdNav!=null;
    boolean pilot = autoPilot!= null;
    System.out.println("VTDGEN - " + gen + "\n" + "VTDNAV - " + nav + "\n" + "AUTOPILOT - " + pilot + "\n");
}

public Configuration(ResourceBundle resourceBundle) {
    try {
        new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
}

public ArrayList<String> stringValue(String xPathExpression) {
    try {
        boolean pilot = autoPilot != null;
        System.out.println("AUTOPILOT - " + pilot + "\n" + "String.length = " + xPathExpression.length() + "\n" + "String = " + xPathExpression);
        autoPilot.selectXPath(xPathExpression);
        int i;
        while ((i = autoPilot.evalXPath())!=-1) {
            stringResult.add(vtdNav.toString(i));
        }
    } catch (XPathEvalException | NavException | XPathParseException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
    return stringResult;
}

public void writeIndex(File indexFile) {
    try (FileOutputStream fos = new FileOutputStream(indexFile)){
        if (parsed) {
            vtdGen.writeIndex(fos);
        }
    } catch (IndexWriteException | IOException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
}

public File createIndexFile(String dir, String filename){
    indexFile = new File(dir + filename + ".vxl");
    writeIndex(indexFile);
    return indexFile;
}

private final String defaultDir = System.getProperty("user.dir") + "/config/";
private final String defaultFileName = "configuration";
private final String defaultXMLFile = defaultFileName + ".xml";
private final String defaultIndexFile = defaultFileName + ".vxl";

private boolean parsed;
private VTDGen vtdGen;
private VTDNav vtdNav;
private AutoPilot autoPilot;
private File indexFile;
private ArrayList<String> stringResult;
}

The result is:

VTDGEN - true
VTDNAV - true
AUTOPILOT - true
AUTOPILOT - false
String.length = 30
String = /configuration/lastMode/text()

The exception:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.NullPointerException
at core.config.Configuration.stringValue(Configuration.java:45)
at UI.PrimaryStageController.test(PrimaryStageController.java:77)
... 57 more

Why does autoPilot refer to null?

役に立ちましたか?

解決

My guess is that you're calling this constructor:

public Configuration(ResourceBundle resourceBundle) {
    try {
        new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        e.printStackTrace(); 
    }
}

That doesn't do what you think it does. It tries to create a new Configuration object, but doesn't actually do anything with it. Regardless of whether that constructor completes successfully or not, all the instance variables in this instance will have their default values.

You could chain to the other constructor instead using this - but then you'll have to declare that this constructor can throw the same checked exceptions:

public Configuration(ResourceBundle resourceBundle)
    throws IOException, IndexWriteException, IndexReadException {
    this(defaultDir, defaultFileName, resourceBundle);
}

You can't catch exceptions when you chain to another constructor.

If you really (really, really) want to suppress exceptions like this, you should probably turn this into a static method, and make sure you return a reference to the newly-constructed-with-actual-data object.

public static Configuration fromResourceBundle(ResourceBundle resourceBundle) {
    try {
        return new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        // Swallowing exceptions? Really?
        e.printStackTrace();
        // At least the caller will get a null reference back instead of
        // a broken object...
        return null;
    }
}

他のヒント

Use this:

public Configuration(ResourceBundle resourceBundle) throws Exception {
    this(Configuration(defaultDir, defaultFileName, resourceBundle));
}

In constructor:

public Configuration(ResourceBundle resourceBundle) {
    try {
        new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
}

You created another instance of Configuration which gave you the output

VTDGEN - true
VTDNAV - true
AUTOPILOT - true

And you're calling method on another instance which initialized with default values for fields

EDIT

As Jon Skeet noted, you need to throw exception. Swallowing exception in constructor is a bad practice, since you successfully return from the constructor and object wasn't initialized correctly

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top