Itai Iloni
Itai Iloni

Reputation: 11

How do you make variables made inside a method available to all other methods in a class?

I'm a coding newb and want to learn some more java. I'm currently taking an introductory programming course in high school and am just trying to play around outside of class. This program I'm creating is supposed to print out a few instructions and questions in the console, to which I want to retrieve user input using a scanner and variable. The goal of the program is for the user to be able to create a 'drawing' based on pure instinct, then this drawing is displayed after the fact.

Here's my problem. I'm asking the user and storing the information in variables that are native to the main method, but I need to use this information in the paintComponent method.

I tried adding static in front of the variable such as: static int emptyRectW1 = kboard.nextInt(); but this just shows the error "Illegal modifier for parametic emptyRectW1; only final is permitted."

I knew it was a long shot trying that out, but that's the best I've got.

I'm using Java Eclipse as my IDE. Thanks for any help, looks like the only way to learn is to mess up and have somebody else point out my mistake! :)

Here's the code:

import java.util.Scanner;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class BlindDrawing extends JPanel {
    public static void main (String[] args) {
        Scanner kboard = new Scanner(System.in);
        System.out.println("Welcome to the Blind Drawing Game!");
        System.out.println("In this game, you will be asked questions about how to construct an image using rectangles and ovals. You will be asked the shape's dimensions and position. The origin (0,0) is the top left corner.");
        System.out.println("First you're making an empty rectangle, how wide do you want it to be?");
        int emptyRectW1 = kboard.nextInt();
        System.out.println("What about the height?");
        int emptyRectH1 = kboard.nextInt();

    }
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawRect(0,0,emptyRectW1, emptyRectH1);
    }
}

Upvotes: 0

Views: 144

Answers (4)

Mark Adelsberger
Mark Adelsberger

Reputation: 45789

Update: In the comments an example was requested, so here's a simple one. This is just a simple demonstration of the ideas I talk about; it does not build on the above code. The reason is that, as others have noted, there are additional things you'd want to correct about the structure of that code. Rather than try to address them in detail, or give an example that encourages them, I'll just focus on the points I was making in my original answer:

public class SampleObject {
    private int aValue;

    protected void getVAlue() {
        Scanner kboard = new Scanner(System.in);
        System.out.println("Enter a value:");
        aValue = kboard.nextInt();
    }

    protected void printValue() {
        System.out.println("The value is " + aValue);
    }

    public void processValue() {
        System.out.println("Welcome");
        getValue();
        printValue();
    }

    public static void main(String[] args) {
        SampleObject sampleObject = new SampleObject();
        sampleObject.processValue();
    }

}

Some styles would have the main method in a separate "application class"; some frameworks have such a thing, wich might have responsibilities like handling outside messages requesting the app shut down, or whatver.

And generally as your applications get beyond simple exercises, you'll want to have more than just one class/object - each with distinct responsibilities that make up your application's functionality.

But for now, this should show how instance data woriks, and how to bootstrap a collection of objects rather than doing lots of processing in a static main method.

Good luck!

As a general answer to the question: variables defined inside a method are not available to other methods, and this is by design of the language. (Some advanced features twist this rule a little, but not in the way you want; and those things are a bit down the road from where you are anyway.)

To share state among methods, you add data members (sometimes called fields) to the class itself. Then values can be assigned in one method and read in another, or whatever, throughout the class.

However, as soon as you try to use that advice you're going to run into another problem, because main() is (and must be) static and your other method isn't (and probably shouldn't be). So you're going to do one of two things to fix that - the easy thing, or the right thing.

The easy thing would be to add static data members - making the data belong to the class rather than an instance of the object. The non-static methods could still see those. But this is leading you in the wrong direction.

The right thing is to do nothing more than bootstrap your application in the main method. What this means is, create object instances that can collaborate to carry out your program's function, and then set those objects in motion. Then all the actual work - like prompting for user input, etc. - should happen in (generally non-static) methods of those objects.

It's a very surface explanation, but really this isn't the place for an in-depth programming lesson. If your curiosity is getting ahead of the coursework, there certainly are good tutorials for OO programming and Java online (or at your local book store... though I may be dating myself with that suggestion).

Good luck.

Upvotes: 4

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

The simple answer: pass the information obtained in the main method into the BlindDrawing object via either a method or a constructor's parameters.

But having said that you really don't want to mix a linear console type application with an event driven GUI app. Choose one or the other and stick with it. Here you can get the information you need via JOptionPanes if you need to get them before launching the main GUI application, and then again pass them into the main GUI application as described above -- via constructor parameters.

For example:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;

import javax.swing.*;

public class BlindDrawing extends JPanel {
    private Rectangle rectangle;

    public BlindDrawing(Rectangle rectangle) {
        setPreferredSize(new Dimension(1000, 800));
        this.rectangle = rectangle;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (rectangle != null) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.RED);
            g2.draw(rectangle);
        }
    }

    private static void createAndShowGui(Rectangle rect) {
        BlindDrawing mainPanel = new BlindDrawing(rect);

        JFrame frame = new JFrame("BlindDrawing");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        String message = "Enter 4 numbers for X, Y, Width, and Height, separated by a space";
        String title = "Get Rectangle Coordinates";
        int messageType = JOptionPane.PLAIN_MESSAGE;
        String input = JOptionPane.showInputDialog(null, message, title, messageType);
        String[] tokens = input.split("\\s+");
        if (tokens.length == 4) {
            int x = Integer.parseInt(tokens[0]);
            int y = Integer.parseInt(tokens[1]);
            int width = Integer.parseInt(tokens[2]);
            int height = Integer.parseInt(tokens[3]);
            Rectangle rect = new Rectangle(x, y, width, height);
            SwingUtilities.invokeLater(() -> createAndShowGui(rect));
        }
    }
}

EDIT

You asked in comments:

What I don't understand are the ones towards the bottom that have tokens. I don't really understand what these tokens do, if you could clarify, that would be great!

The question for me is: how can we most easily allow the user to enter for ints to use in a Swing application, but to sub-divide this even further we can ask for solutions that are most easy for the coder and solutions that are most easy for the user. In general you will want to prefer going after the latter.

The easiest for the coder is to create a simple console application and have the user enter the numbers using String prompts and a Scanner object that has been initialized to the standard input or System.in. As I've mentioned, while this is easy for the coder, it does not work well for the user since console applications and GUI applications don't play nice together as one is structured in a linear fashion while the other is much more free-flowing and event driven. If you want to get data later on while the program runs, and you again have the user enter information through the console, you risk either freezing the Swing application, rendering it useless, or updating the Swing application inappropriately in a background thread.

My solution above is a compromise where we prompt the user for a single String by using a JOptionPane.showInputDialog(...). This will give the user a prompt, and allow him to enter a single String into a text field. Here I ask the user to enter four numbrers separated, each separated by a space, and I think that you understand this part, but one problem with it is, once you get that String, which I call input, how do you extract the four numbers out of it.

One way is to split the String into an array of Strings (which I call tokens) using String's split(...) method. A simple way to do this is like so:

String[] tokens = input.split(" ");

This will split the input String into an array of sub-strings by splitting on a single space. It doesn't work so well if the user accidentally uses more than one space between his numbers, or if he uses different "white-space" to separate the numbers. I instead used:

String[] tokens = input.split("\\s+");

This uses something called regular expressions or "regex" for short to split the String. \\s represents any white-space character, and the + means that we'll split on one or more white-space characters. So if the user puts 2 or 3 spaces between his numbers, this still will work.

Another possible way to split this String is to put it into a Scanner and then "scan" for ints. Here we don't Scan with the standard input, with System.in, but rather we scan the entered String, here input. For example:

// get our line of input same as we did before
String input = JOptionPane.showInputDialog(null, message, title, messageType);

// create a Scanner object that scans through the line
Scanner inputScanner = new Scanner(input);

// extract the four numbers from the line
int x = inputScanner.nextInt();
int y = inputScanner.nextInt();
int width = inputScanner.nextInt();
int height = inputScanner.nextInt();

// close the Scanner so we don't waste resources
inputScanner.close();

There are still problems with this since all these solutions fail if the user enters 2 or 3 numbers and not 4, or if the user enters non-numeric input, and there are other ways of checking for this and correcting, such as using a JSpinner or JComboBox that limits the user's selections to allowed numbers, or by using try/catch blocks to catch invalid input and then prompt the user for more correct input.

Upvotes: 3

Keith OYS
Keith OYS

Reputation: 2305

You can initialize them as instance variables so they can be used by various other methods, like so:

public class BlindDrawing extends JPanel {
    private int emptyRectW1;
    private int emptyRectH1;

    public static void main (String[] args) {
        new BlindDrawing().run();
    }

    private void run() {
        Scanner kboard = new Scanner(System.in);
        System.out.println("Welcome to the Blind Drawing Game!");
        System.out.println("In this game, you will be asked questions about how to construct an image using rectangles and ovals. You will be asked the shape's dimensions and position. The origin (0,0) is the top left corner.");
        System.out.println("First you're making an empty rectangle, how wide do you want it to be?");
        emptyRectW1 = kboard.nextInt();
        System.out.println("What about the height?");
        emptyRectH1 = kboard.nextInt();
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawRect(0, 0, emptyRectW1, emptyRectH1);
    }
}

Upvotes: 0

Actually, a variable declared inside a method is a local variable.

Please extend the scope of the the two variables just before your main method. See code below.

static int emptyRectW1, emptyRectH1;
public static void main (String[] args) {

Then manipulate the latter code to avoid duplicate variable declaration as shown below.

    emptyRectW1 = kboard.nextInt();
    System.out.println("What about the height?");
    emptyRectH1 = kboard.nextInt();

I hope this helps.

Upvotes: -2

Related Questions