Reputation: 28
The same type of scanner method is working in one place, but not in the other... I am using eclipse as my code editor if that changes any answers. All variables have been declared elsewhere if not here.
If anyone knows how to fix this, you will have my undying gratitude :)
This one is working:
private String questionPicker(String str)
{
question = (int)(Math.random()*42);
System.out.println(question);
fileChoose = str.toUpperCase();
String returnee;
Scanner is = null;
try
{
is = new Scanner(new FileInputStream("triviaQ"+fileChoose+".txt"));
}
catch(FileNotFoundException z)
{
System.out.println("Error 004: File retrieve failed.");
}
skipLines(is, question);
returnee = is.nextLine();
is.close();
return returnee;
}
This one is not working:
public String getAnswer()
{
String returnee;
Scanner ls = null;
try
{
ls = new Scanner(new FileInputStream("triviaA"+fileChoose+".txt"));
}
catch(FileNotFoundException z)
{
System.out.println("Error 004: File retrieve failed.");
}
skipLines(ls, question);
returnee = ls.nextLine();
ls.close();
return returnee;
}
error message:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot invoke "java.util.Scanner.nextLine()" because "ls" is null
at jepp.JepQA.getAnswer(JepQA.java:60)
at jepp.JepGui.actionPerformed(JepGui.java:268)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6617)
at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
at java.desktop/java.awt.Component.processEvent(Component.java:6382)
at java.desktop/java.awt.Container.processEvent(Container.java:2264)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4993)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2322)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4825)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4934)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4563)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4504)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2308)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2773)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4825)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Upvotes: 1
Views: 455
Reputation: 9377
Invest in debugging NullPointerExceptions (also known as NPE). You may consult the link commented by Progman.
Follow the Exception (here NPE) from top to bottom:
java.lang.NullPointerException: Cannot invoke "java.util.Scanner.nextLine()" because "ls" is null
at jepp.JepQA.getAnswer(JepQA.java:60)
at jepp.JepGui.actionPerformed(JepGui.java:268)
What you can see from the top 3 lines in stack-trace:
Scanner
variable ls
was null. That's why any method invocation on it like ls.nextLine()
will fail with a NPE.JepQA
, inside the getAnswer
method, at line 60 of the filegetAnswer
was called by an action-handler, method actionPerformed
in your JepGui
null
?In the method getAnswer
you initialize ls
as null
- that's OK.
Then, inside the try-block, you open the Scanner on a FileInputStream connected to the file given by path:
try {
ls = new Scanner(new FileInputStream("triviaA"+fileChoose+".txt"));
} catch(FileNotFoundException z) {
System.out.println("Error 004: File retrieve failed.");
}
This Scanner assignment, exactly the opening of the InputStream has failed, thus ls
became null
here again.
Reasons for that may be:
String
argument passed to FileInputStream
constructor was null
. Check or debug-print "triviaA"+fileChoose+".txt"
. Also make sure that fileChoose
itself is not null
, otherwise the whole concatenated expression passed as argument will be null
too.FileNotFoundException
when opening the stream or Sanner. But then, why wasn't it catched and printed as error?fileChoose
is null
and as a result the concatenated pathname (file-name), too. Then read from the docs of constructors FileInputStream(String) or FileInputStream(File)
, that it might internally create a File
from the pathname given as String
and see the explanation there:Throws:
NullPointerException
- If the pathname argument isnull
Update: Thanks to k314159's comment falsifying reasoning on my assumptions:
fileChoose = null
then concatenated path argument would be "triviaAnull.txt"
.Explanation:
(3) was tested in null-concatenating demo. Hence, if invalid path argument passed, this causes a FileNotFoundException
at line ls = new FileInputStream(..)
so that observed ls
NPE could never have been reached. Instead thrown exception must have been catched. Since no return handling was implemented the error prints to console (not shown on GUI) and flow continues with erroneous ls = null
until observed NPE thrown at ls.nextLine()
.
So (2) must be the most-probable root-cause. However the FileNotFoundException
could have been caused by fileChoose = null
or any other invalid path passed to construct the Scanner instance.
Your file-name passed as argument pathname to FileInputStream
constructor was null might have been invalid, either, because the fileChoose
variable was null
or else.
Compare the other working method questionPicker(String str)
where you assign it as fileChoose = str.toUpperCase();
. If you would pass a str
as null
here, then it may also throw a NPE.
Add a guard-statement (see Fail-fast principle).
For example using Objects.requireNonNull(fileChoose, "answer/question file must not be null");
where the second is the error-message printed when fileChoose
is null.
See also: Why should one use Objects.requireNonNull()?
As Bohemian's answer correctly spotted the design-issue:
ls
related)In case those were to roughly outlined, here a complete before/after example with explaining comments.
Before:
String returnee; // consider a default return value to avoid any null at the end
Scanner ls = null; // a null Scanner is useless, consider initializing inside try (local scope is safer)!
try { // try block can encompass the whole attempt with every dependent statement
ls = new Scanner(new FileInputStream("triviaA"+fileChoose+".txt")); // test filename at least for non-null, before try block!
} catch(FileNotFoundException z) { //
System.out.println("Error 004: File retrieve failed."); // perfect to log
// but how to handle: return or assign returnee with a default
}
skipLines(ls, question); // depends on ls, so put inside try-block
returnee = ls.nextLine(); // depends on ls, so put inside try-block
ls.close(); // safe resources: close inside finally-block!
return returnee; // what if this is (still) null
Incorporating the second outlined pattern from Bohemian:
After:
// default return
String returnee = ""; // default return value
// guard-statement with fail-fast
if (fileChoose == null) {
return returnee; // returns the default or throw
}
String filename = "triviaA"+fileChoose+".txt"; // can not fail, because validated before. Worst case, e.g. least "triviaA.txt"
try { // block of ls dependent statements
Scanner ls = new Scanner(new FileInputStream(filename)); // filename not-null
skipLines(ls, question); // safer, because in local/try scope
returnee = ls.nextLine(); // safer, but may throw NoSuchElementException
} catch(FileNotFoundException notFound) { //
System.out.println("Error 004: File retrieve failed: " + notFound.getMessage()); // perfect to log
// but how to handle: return or assign returnee with a default
} catch(NoSuchElementException noElement)
// log
// and handle!
} finally {
if (ls != null) ls.close(); // always close (if not null)
}
return returnee; // Avoid null, at least a default. Benefit: no NPE-risk for the caller.
See also: What Sanner.nextLine()
might throw
Upvotes: 1
Reputation: 425073
Just catching exceptions is not enough; you must also change the code path accordingly.
Your code has the form:
Scanner ls = null;
try {
ls = new Scanner(...);
} catch(FileNotFoundException z) {
// print error message
}
// do something with ls
If the exception happens, execution continues after printing the error message and ls
will still be null when the do something with ls code executes, which is why you got the NullPointerException
.
To fix, either exit the method on exception:
Scanner ls = null;
try {
ls = new Scanner(...);
} catch(FileNotFoundException z) {
// print error message
return;
}
// do something with ls
Or move the code that uses ls
inside the try
:
try {
Scanner ls = new Scanner(...);
// do something with ls
} catch(FileNotFoundException z) {
// print error message
}
Note also with the last version, not only do we save a line of code (assignment and declaration as one line), but the scope of ls
is limited to the try
block; limiting scope is good coding practice.
Upvotes: 3