Accessing Scanner objects from subclass in a superclass? - java

I am a very new, relatively inexperienced Java programmer. The project I am working is just a test of my current skills, and my goal is to write as efficient a program as possible.
In essence, I have three classes: A, B, and C. B extends A and C extends B, but I want a Scanner object in C to be used in a switch statement (part of a larger method) in A.
The reason I want this is because I do not want to overload the method in A (copy-pasting the same code with different parameters is not ideal), and I do not want to combine all of my classes into one (the code is simple enough to do this, but I want to test my knowledge of object creation and use).
Here is some of the code:
import java.time.LocalDateTime;
public class WatchFace {
// MASTER TIME
LocalDateTime dateTimeObject = LocalDateTime.now();
int hour = dateTimeObject.getHour();
int minute = dateTimeObject.getMinute();
// WATCH FACE METHOD
public void watchFaceMethod() {
// Code I'd like to utilize; this is my question for StackOverflow
// switch (userInput) {
// case 1:
// // Intentionally do nothing
// break;
//
// case 2:
// // Change minute and hour to some values obtained by timezone stuff
// break;
//
// case 3:
// // Change both minute and hour to -1
// break;
// }
// Basically, the rest of this code just prints something different to the Windows CLI depending on the
// hour and minute variables' current values (i.e. beyond the intended switch statement).
}
}
import java.time.format.DateTimeFormatter;
public class Watch extends WatchFace {
static void watchMethod() {
// Code printing some Strings is here.
WatchFace watchFaceObject = new WatchFace();
watchFaceObject.watchFaceMethod();
// Code printing some more Strings is here.
DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("hh:mm a 'on' EEEE, MMMM dd, yyyy");
String dateTimeDisplay = watchFaceObject.dateTimeObject.format(dateTimeFormat);
System.out.print("\nIt is currently " + dateTimeDisplay + "\n");
if (watchFaceObject.hour == 11 && watchFaceObject.minute == 11) {
System.out.println("Make a wish!");
}
}
}
import java.util.Scanner;
public class InteractiveWatch extends Watch {
public static void main(String[] args) {
// WATCH OBJECT
Watch watchObject = new Watch();
// STARTUP
System.out.println("Welcome to Interactive Watch!\n");
System.out.println("What would you like to do?");
System.out.println("[1] See local time.");
System.out.println("[2] See local time in a particular place.");
System.out.println("[3] See something special.\n");
Scanner scannerObject = new Scanner(System.in);
// INPUT
boolean loopBreak = true;
while (loopBreak) {
loopBreak = false; // loopBreak set to false
String userInput = scannerObject.nextLine(); // User inputs some string
switch(userInput) {
case "1":
watchObject.watchMethod(); // watchFaceMethod ideally detects userInput == 1
break;
case "2":
watchObject.watchMethod(); // watchFaceMethod ideally detects userInput == 2
break;
case "3":
watchObject.watchMethod(); // watchFaceMethod ideally detects userInput == 3
break;
default:
loopBreak = true; // loopBreak set to true; while loop reinitiates
System.out.println("\nPlease enter a valid key.\n");
break;
}
}
}
}
I learned everything I have from w3schools' Java course, but I still have much more to learn. Let me know if what I want is even possible, or anything else that would make this code more efficient. Thank you!

The short answer is no. You cannot access an object belonging to a subtype.
The long answer: The watchFaceMethod does not know that the call came from an InteractiveWatch. Think about it this way; what if we made a new class OtherWatch, which also extends Watch. Suppose OtherWatch does not have a Scanner object. Now what is watchFaceMethod() going to do when it's told to invoke a method of your Scanner object? It can't do anything, because the Scanner object does not exist. I'm not sure I understand why you're trying to access the Scanner object in watchFaceMethod in the first place, though. You already got the input from the user. You don't want to get more input, so you really want access to the string that the nextLine() method returned. I would approach this by simply passing the string up the hierarchy as a parameter to the watchMethod() and watchFaceMethod() methods. It's not "inefficient" to pass parameters to another method. You would end up with methods something like these:
public void watchMethod(String userInput) {
...
WatchFace watchFaceObject = new WatchFace();
watchFaceObject.watchFaceMethod(userInput);
...
}
and
public void watchFaceMethod(String userInput) {
switch (userInput) {
case "1":
...
break;
case "2":
...
break;
case "3":
...
break;
}
...
}
Another option is to make userInput a public, static variable, and then just reference it from watchFaceMethod(), but I would advise against this as you could quickly lose track of what methods are accessing and mutating that variable.
One more little thing I noticed about your code; you use \n for your line separators, which produces a linefeed character. This is the standard line separator on UNIX systems, but Windows uses a carriage return together with a linefeed, and OSX uses just the carriage return, so if you want your returns to show up on all platforms, you should use %n, which produces the correct platform-specific line separator.

No, it's not possible.
superclass does not have access to members of its subclasses in Java. But the subclass has access to all non-private members of its superclass.

Related

What's a better or more standard way to perform this function?

I am a java beginner, and in this particular problem I practiced making a program that converts any given string to lowercase string. Is there a a better way to achieve this goal in java (in terms of design)?
Also, how does the "else" (after "else if") catches or waits for me to make an input. Somehow that part does not make sense to me, even though I achieved what I wanted. How is the value of "ans" from input transferred to the entire loop and used until the loop is closed?
After many attempts and failures, I used a separate method for the conversion part. My second question is a bit too complicated to be researched.
import static java.lang.System.out;
import java.util.Scanner;
public class MyClass {
public static Scanner s = new Scanner(System.in);
public static String ans;
public static void main(String args[]) {
Conversion();
do {
ans = new String(s.nextLine());
if (ans.equalsIgnoreCase("Y")) {
Conversion();
} else if (ans.equalsIgnoreCase("N")) {
out.println("Thank you for using this program!");
break;
} else {
out.println("Invalid entry!");
out.println("Would you like to convert another string?\n(Please type 'Y' for yes, or 'N' for no.)");
}
} while (ans != "N");
}//END MAIN
public static void Conversion() {
out.println("Please enter string to be converted to lowercase: ");
String str = new String(s.nextLine());
out.println("Your new string is: " + str.toLowerCase());
out.println("Would you like to convert another string? (Y/N)");
}
}
I notice a few issues; Conversion looks like a class-name (Java naming convention would be conversion) and ans != "N" is using == instead of .equals - and wouldn't ignore case (!ans.equalsIgnoreCase("N")). Globals (e.g. static) are bad (pass the Scanner to the methods that need it), and the static import just makes the code more difficult to reason about (in my opinion). Your current loop doesn't really follow a conventional form, I would extract the prompt and loop for "another" conversion to a new method and if you must print a thank you I'd do so after the "main loop". Something like,
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
do {
conversion(sc);
} while (another(sc));
System.out.println("Thank you for using this program!");
}
public static void conversion(Scanner s) {
System.out.println("Please enter string to be converted to lowercase: ");
System.out.printf("Your new string is: %s%n", s.nextLine().toLowerCase());
}
public static boolean another(Scanner s) {
while (true) {
System.out.println("Would you like to convert another string? (Y/N)");
String ans = s.nextLine();
if (ans.equalsIgnoreCase("Y")) {
return true;
} else if (ans.equalsIgnoreCase("N")) {
return false;
}
System.out.println("Invalid entry!");
System.out.println("(Please type 'Y' for yes, or 'N' for no.)");
}
}
Answering your first question:
There are many design patterns and practices so many people can argue what I would recommend you to do. It's basically the same for all programming languages. Let's take your function "Conversion". The name itself says that you use it to convert stuff. Not to display, not to prompt - to convert. In this case, the only actual thing it should do is to convert upperCase to lowercase. In fact, you might want to specify what type of conversion it has in the name of the function (convertToLowerCase?). In fact, in Java, we use lowerCamelCase for all function names and UpperCamelCase for classes.
If you accept my previous suggestion, you could break the Conversion function into multiple ones like promptUserForInput, WrongInputHandler and so forth.
If I understood your second question correctly, you wonder about the way the code executed and how the variable ans is transferred further into the loop. Let's take a look at your code and what variables do:
You initialize your variable in the class MyClass by making it accessible to all methods in the class;
You prompt the user for the input to assign to this variable inside the do..while loop with this line ans = new String(s.nextLine()); which saves the value of the variable and, again, which can be accessed inside the whole class so its value is changed.
It goes into the if..else if...else statement. The way it works, it goes line by line - if the first if-statement fails, it goes on until it finds a truthy statement and it doesn't go any further. In your case, if the ans is not equal to either y/Y/ it will go to else if statement and if it's not n/N, it will go to else (so basically whatever except y/Y/n/N) and will be executed. After that, it jumps into the while (ans!= "N"); line where it compares your class-member variable ans and if it's not equal to "N" it starts over the loop right after the do{ part until you type in the "N".
I hope that makes sense. Whenever the program is asking you for input, it does not execute code further but is stuck until you provide any input. The value from input itself isn't passed throughout the loop and the program. The reason why you can use it because you created a higher-scope variable ans where you saved the result of your input.
IMPORTANT: if you've declared the ans inside the do..while loop, you would've not been able to have accessed it in the while (ans...) because it will 'die' right before the curly brace between do { ...here} while(). If you want to learn more about the scope and variables in general, you can read this article.
Here is my code example:
public static void main(String args[]) {
//declare before entering the loop to have higher scope
String ans = "y";
do {
//we get the given string to convert from the user
String str = promptForString();
//we convert the string
str = converseStringToLowerCase(str);
//display the string (could use another function for that: easier to debug and locate problems and in bigger projects)
out.println("Your new string is: " + str);
//prompt user for respond to continue or not
ans = promptForContinue();
handleResponse(ans);
} while (!ans.equals("n"));
}//END MAIN
//prompts user for an input string
public static String promptForString() {
out.println("Please enter string to be converted to lowercase: ");
String str = new String(s.nextLine());
return str;
}
//converts any given string to lower case
public static String converseStringToLowerCase(String str) {
return str.toLowerCase();
}
//is used to prompt user for reply
public static String promptForContinue() {
out.println("Would you like to convert another string? (Y/N)");
String str = new String(s.nextLine());
//is good to make if...else statements easier - it will always be lower case (or upper if you prefer)
return str.toLowerCase();
}
//easier to locate other response scenarios
public static void handleResponse(String response) {
if (response.equals("n")) {
out.println("Thank you for using this program!");
//it's not a very good practice to innaturally break loops. Use input for that in while(..) statement
// break;
} else if (!response.equals("y")) {
out.println("Invalid entry!");
out.println("Would you like to convert another string?\n(Please type 'Y' for yes, or 'N' for no.)");
}
}

Compare enum values to strings java [duplicate]

I'm making (my own version of)roulette with Java, and one of the types of bets a player can make is to choose the color that is going to be rolled. (Even is black, odd is red). Is there a way I can use a switch statement to compare a string against an enum?
private enum colors{red, black};
private String colorGuess;
private boolean colorVerify = false;
public void getColorGuess(){
do{
Scanner in = new Scanner(System.in);
colorGuess = in.nextLine();
switch(colors){
case red:
colorVerify = true;
break;
case black:
colorVerify = true;
break;
default:
System.out.println("Invalid color selection!");
break;
}while(colorVerify = false);
This is what i'm trying to get but it's not letting me use the enum 'colors' in a switch statement.
You must have an instance of an enum type (its member) on which you switch. You are trying to switch on the Enum class itself, which is a meaningless construct. So you probably need
colors col = colors.valueOf(colorGuess);
switch (col) ...
BTW the name should be Colors, not colors to respect the very important and non-optional Java naming convention.
You can get an enum from a string with Enum.valueOf(). Take care here, the other answers fail to mention that Enum.valueOf() will throw an IllegalArgumentException if passed an string which isn't a valid member of the enum.
Be sure to properly format and indent your code, it helps us (and you!) read it and understand what's going on:
// note the capitalization, and the singular 'Color'
private enum Color {RED, BLACK};
// At least with the code provided, you don't need colorGuess or colorVerify to be
// instance variables, they can be local to the method. Limiting the amount of
// time a variable lives for (its scope) is critical for quality, maintainable code
public Color getColorGuess() {
Scanner in = new Scanner(System.in); // this should be outside the while loop
while(in.hasNextLine()) {
// .toUpperCase() lets you type "red" or "RED" and still match
String line = in.nextLine().toUpperCase();
try {
// Enum.valueOf() throws an exception if the input is not valid
Color guess = Color.valueOf(line);
switch(guess) {
case RED:
return guess; // return, rather than break, to exit the method
case BLACK:
return guess;
// As long as your switch statement covers all cases in your enum, you
// don't need a default: case, you'll never reach it
}
} catch (IllegalArgumentException e) {
System.out.println("Invalid color selection!");
}
}
}
Note that we now return guess in both cases, which is somewhat redundant. At least with the example code you provided, you don't actually need to track colorVerify at all, because the method will keep looping forever until a valid color is entered. You could replace the whole switch statement in my method with simply return guess; as you know it's a valid guess as soon as Color.valueOf() returns a value.
In other words, you could clean your code up to:
public static Color getColorGuess() {
try (Scanner in = new Scanner(System.in)) {
while(in.hasNextLine()) {
try {
return Color.valueOf(in.nextLine().toUpperCase());
} catch (IllegalArgumentException e) {
System.out.println("Invalid color selection!");
}
}
}
}
Notice that the method is static now, and uses a try-with-resources block to close the Scanner once you're done with it.

Printing contents of file based on user input

Trying to print a file based off the user's input as mentioned in the title. Basically, my program has been altered from one that I previously created which reads data from a file, so I know that the file has been imported correctly (not the problem).
The problem I have is that I'm trying to make the program print the entirety of the .txt file if the user chooses a specific number, in this case '1'. My current code so far is:
import java.io.FileReader;
import java.util.Scanner;
public class InputOutput {
public static void main(String[] args) throws Exception {
// these will never change (be re-assigned)
final Scanner S = new Scanner(System.in);
final Scanner INPUT = new Scanner(new FileReader("C:\\Users\\JakeWork\\workspace\\Coursework\\input.txt"));
System.out.print("-- MENU -- \n");
System.out.print("1: Blahblahblah \n");
System.out.print("2: Blahblahblah \n");
System.out.print("Q: Blahblahblah \n");
System.out.print("Pick an option: ");
if (S.nextInt() == 1) {
String num = INPUT.nextLine();
System.out.println(num);
}
I feel as if my if statement is totally off and I'm heading in the entire wrong direction, could anyone point me in the right and give me a helping hand?
You're close, but not quite there.
You a reading the user input correctly, but now you need the file contents in a loop.
if(S.nextInt() == 1) {
while (INPUT.hasNextLine()) {
System.out.println(INPUT.nextLine());
}
}
This will keep looking as long as the file contents hasNextLine
You can safely remove the String option = S.next();
Also, just a small bit of naming convention nitpicking, don't use all upper case letters for variable names unless they are meant to be static. Also, the first letter of a variable is generally lower case.
if (S.nextInt() == 1) {
// check if there is input ,if true print it
while((INPUT.hasNextLine())
System.out.println(INPUT.nextLine());
}
Also, for menu scenarios like this, consider using a switch statement, then place a call to the menu-printing (that you move to a separate method) in the default case, so that if you enter something wrong, you can reprint the menu choices. Also, the switch statement is more readable (arguably) than a bunch of if's, like this:
int option = S.nextInt();
switch(option) {
case 1 :
while(INPUT.hasNextLine()) {
System.out.println(INPUT.nextLine());
}
break;
case 2 :
//Do stuff
break;
default :
//Default case, reprint menu?
}
}

How do I repeat code for a certain case in a switch statement and not repeat it for others?

I am making a Text-Based Adventure game. In one room, I want to ask the player what to do, and if he goes through the door he/she moves to the next room. If he/she picks up the flashlight, the "what do you want to do?" question is repeated so they can go through the door after that. Here is my code:
case 1:
System.out.println("What do you want to do? Type 'door' to go through the door or 'flashlight' to pick up the flashlight.");
String roomOneAction = input.nextLine();
switch(roomOneAction) {
case "flashlight":
System.out.println("Picked up flashlight.");
//repeat what do you want to do? question
break;
case "door":
System.out.println("You open the door.");
//continue plot, don't repeat question
break;
default:
System.out.println("Error");
// repeat what do you want to do? question
break;
}
break;
With this code right now, it repeats the question for anything you type. I do not want this to happen if "door" is typed.
Also, how can I make it so that once the flashlight is picked up and the question is repeated, the flashlight can't be picked up a second time?
This might be close to what you want:
import java.util.Scanner;
public class Game {
private static boolean flashlightPicked = false;
private static boolean doorEntered = false;
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while (true) {
int x = 1;
switch (x) {
case 1:
System.out
.println("What do you want to do? Type 'door' to go through the door or 'flashlight' to pick up the flashlight.");
String roomOneAction = input.nextLine();
switch (roomOneAction) {
case "flashlight" :
if(!flashlightPicked){
System.out.println("Picked up flashlight.");
flashlightPicked = true;
} else {
System.out.println("Already picked up flashlight!");
}
// repeat what do you want to do? question
break;
case "door":
if(!doorEntered){
System.out.println("You open the door.");
doorEntered = true;
}else{
System.out.println("Already opened the door!");
}
// continue plot, don't repeat question
break;
default:
System.out.println("Error");
// repeat what do you want to do? question
break;
}
}
}
}
In this case you should probably store the information if you want to ask again or not:
boolean askAgain = true;
//...
case "door":
//...
askAgain = false;
//...
if (askAgain) {
System.out.println("...");
}
I would recommend using a while loop here. Can the player do anything else in the room or leave the room in a different way? If they can only leave the room using the 'door' command, set up the while loop to have the terminating factor being the 'door' command.
I don't like the idea of cascading multiple switches as you did. You should use state pattern and command pattern, but I assume you're a programming beginner, so I'll just try to answer your question.
Wrap the roomOneAction switch statement in a while loop, like
boolean foundFlashlight = false;
boolean askAgain = true;
while (askAgain)
{
// question + switch statement and so on
}
if the player picks the flashflight, set foundFlashlight to true.
In the next run, the player gets asked again. If he picks flashlight,
you have to evaluate the foundFlashlight variable, if the value is true,
tell the player he already has the flashlight and he should answer again.
In the door case, simply set askAgain to false. The loop while terminate then.
But again, this is not a nice, clean and easy to maintain approach. You should take a look at some design patterns. Especially the above mentioned state pattern.

How to make a string compare non case sensitive?

I am trying to write a code for one of those programs that responds according to your answers. I want to make it so that some of the variables are not case sensitive. For example if my variable x is equal to "Me" I want it to also equal "me". Is that possible?
Here is my code so far:
import java.util.Scanner;
class Tutorial {
public static void main (String args[]){
System.out.println("Who goes there?");
Scanner N = new Scanner(System.in);
String name = N.next();
if (name.equals("me") || name.equals("Me")){
System.out.println("Well, good for you smartass.");
System.exit(1);
}else System.out.printf("Nice to meet you, %s.%n", name);
System.out.print("How are you doing?");
Scanner d1 = new Scanner(System.in);
String doing = d1.next();
switch(doing){
case "good": System.out.println("that is nice to hear.");
case "Well": System.out.println("that is nice to hear.");
case "bad" : System.out.println("That's ruff mate.");
case "Awesome" : System.out.println("Nice");
case "Terrible" : System.out.println("Sucks for you");
}
}
}
I don't want to have to make 2 cases for each answer where one is uppercase and the other is lower case.
Slightly off topic question. How do I close the resource leaks for the scanner?
Worth mentioning String#toLowerCase:
name.toLowerCase().equals("me");
Or simply use String#equalsIgnoreCase:
name.equalsIgnoreCase("me");
I think there is no way to do no case sensitive variable. From your code you want to threat answers in strings into a switch and do some action that depends on answer.
I think the best way is to take user input a use on that string upper method from string class.
YourString = YourString.toUpperCase();

Categories

Resources