I need to write a program that can draw stuff on a frame, let's say, trees.
Their IDs, colors, positions etc. are in a txt file, line by line, the first word of the line is the name of their type which will be the name of the subclass.
These parameters are given by the "Trees" class. The drawing method of the various types of trees are written in subclasses.

So I thought I should make a class for getting the data from the text file and then pass the scanner as a parameter to the Trees class' constructor, where the variables are specified.
The problem is, when I'm trying to make the subclasses, i either cant use the variables from the Trees class, or don't know how to use super() with a scanner.

Also, am I supposed to put each one of them in a Map like Map<ID, Trees> after the reading or put them in a Map in the Trees class? How am i supposed to draw each one of them on the background?

Maybe this task is not as complicated as I think it is, so feel free to tell me if you have any better ideas on how i should solve it.

I thought I should make a Trees class like this:

public class Trees {
    public Trees() {} // Probably this is why I can't use the variables in the subclass
    public Trees(Scanner scan) {
        ID = scan.nextInt();
        color = scan.next();
        // Should I put them in a Map here?
    }
    public String color;
    public int ID;
}

class appleTree extends Trees {
    // an apple tree's own paint() method
}

And then my other file looks like this:

public class getData {
    getData(String file)
    {
        Scanner scan = new Scanner(new File(file));

        while(scan.hasNext())
        {
            Trees trees = new Trees(scan);
            // read from file here
        }
    }

    // make JFrame
}

class Drawing extends JFrame {
    // This one has a paint() method that draws the background, how should I draw the trees here?
    // I can see that if I say appleTree a = new appleTree(); a.paint(g); it draws perfectly whatever I did in the appleTree class' paint() method,
    // but that's just a general drawing method for any apple tree, how do I draw that exact apple tree i specified in my txt file?
}

Recommended Answers

All 8 Replies

how do I draw that exact apple tree i specified in my txt file?

What exactly did you do in your AppleTree's paint method?

Should I put them in a Map here?

Nope.

public String color;
public int ID;

Change to private and make getter methods for these. In your subclasses' paint method you can call the getters to get the info you need to draw your tree.

What exactly did you do in your AppleTree's paint method?

I thought it should only contain the drawings, so i tried to make the drawing from shapes like this:

class appleTree extends Trees {
    appleTree(){}

    public void paint(Graphics g)
    {
        int x = 350;
        int y = 120; // I just gave it some exact numbers to test it
        g.setColor(Color.black);
        g.fillRect(x,y,40,100);
        g.setColor(Color.green);
        g.fillOval(x-18,y-20,80,80);
    }
}

So my idea was that every type of tree has its own class that contains the drawing from basic shapes and the other class that draws the background should only draw "an apple tree" not circles and rectangles. So when i get the data from the text file, for eg.
appleTree 6 350 120 green
then it will draw that exact apple tree with the ID of 6 on those exact coordinates and with that given color.

The first problem is, I can't get the read values in the paint method in my appleTree subclass; I tried printing it out and whatever I do it says all values are 0s and "null"s.
The second problem is, I'm not sure how to draw depending on the read values in my class that draws the background. I can use appleTree.paint(g); but that only draws out one tree, I guess I should call that paint() method on every appleTree I just don't see how and where.

Change to private and make getter methods for these. In your subclasses' paint method you can call the getters to get the info you need to draw your tree.

I tried making a

public final int getX(){return x;} 

in my Trees class and then using

Trees trees = new Trees();
int t = trees.getX();

I'm not sure if this is what you mean but t also gave "0" back when i tried printing it out.

The first problem is, I can't get the read values in the paint method in my appleTree subclass; I tried printing it out and whatever I do it says all values are 0s and "null"s.

Trees trees = new Trees();
int t = trees.getX();

This code is taken from your previous post, take a close look at it.
Where do you set values for the fields of your Tree?
Not in your constructor, and you don't call any setter, so it gets initialized to a default value, which is null in case of reference types, and 0 in case of integral types.

I'm not sure if this is what you mean but t also gave "0" back when i tried printing it out.

You have to call getX() and getY() in the paint() method of the Tree object that you initialized with the data from your file.

I can use appleTree.paint(g); but that only draws out one tree, I guess I should call that paint() method on every appleTree

Yes.

I just don't see how and where.

In your class that reads the file you can write a method that returns a Collection of Tree objects. In your GUI code you call that method, and a Collection<Tree> is returned.
Iterate over that collection and call paint() for every Tree.

BTW, throughout my post I refer to your Trees as Tree. Why exactly did you call it that way?

Where do you set values for the fields of your Tree?

I have a class that reads from a file like this:

public class getData {
    getData(String file)
    {
        Scanner scan = new Scanner(new File(file));
        while(scan.hasNext())
        {
            Trees trees = new Trees(scan);
        }
    }

So I thought when I pass "scan" to the constructor of the Trees class and read values like this:

ID = scan.nextInt();

I'll be able to use the read data in my subclasses kinda like this:

class appleTree extends Trees {
    appleTree(){
        this.x = x;
        this.y = y;
        //etc...
        //and then use these values in this appleTree class' paint() method.
    }
}

Every type of tree (there are like 5 of them) has an ID, a color, position, etc. it's just the appearance that is different.
I don't understand the point in using subclasses if I can't use those variables in them, so I'm probably doing something completely wrong since the task is to use subclasses.
Maybe I don't even need to use them here and I just have to call these methods with the proper data after the reading in my other class?

All the common things (id, color, position) go in the superclass since they are the same for every Tree. Like you said: the only thing that differs is the the appearance.
So how should this superclass look?

// Our superclass
public abstract class Tree
{
    private int id;
    private int x;
    private int y;
    private String color;

    public Tree(int id, int x, int y, String color)
    {
        this.id = id;
        this.x = x;
        this.y = y;
        this.color = color;
    }

    // We want each subclass to have a paint method.
    // Since the appearance of each Tree is different,
    // we leave it up to the subclass to implement the
    // drawing behaviour.
    public abstract void paint(Graphics g);

    // getters (public)
    // You need to provide these yourself!
    // ...
}

I omitted the use of setters here for the sake of simplicity, and to keep the example short.

How might we implement an AppleTree?

// A concrete Tree subclass
public class AppleTree extends Tree
{
    public AppleTree(int id, int x, int y, String color)
    {
        // The fields for id, x, y and color are declared as private
        // in the superclass, they are still inherited, but they aren't
        // visible in subclasses.
        // Call the constructor of the superclass and let it handle setting
        // these fields for us.
        super(id, x, y, color);
    }

    @Override
    public void paint(Graphics g)
    {
        // Implement your drawing algorithm here.

        // First problem: we need the x, y and color,
        // but these are declared private in the superclass,
        // so they aren't visible to subclasses. They are still
        // inherited though. How do we get the values for these
        // if they aren't visible to the subclass?
        // Well... that's where the public getter methods come in.
        // These are inherited too, and since that they are declared as
        // public, they are also visible in the subclass! Let's use them.

        int x = getX(); // call the inherited public getter
        int y = getY(); // call the inherited public getter
        String color = getColor(); // call the inherited public getter

        // Add your drawing code...
    }
}

Now we still need to load our Trees from a plain text file.
We can do this by writing a DAO (= Data Access Object):

Let's first define it's interface:

public interface TreeDao
{
    public Tree getById(int id);
    public Collection<Tree> getAll();
}

The above interface specifies a contract. In this contract we say: "I want to be able to get Trees by their id, and I also want to be able to get a collection of all Trees".

Let's provide an implementation for this interface, an implementation that reads all the Trees from a plain text file:

public class PlainTextFileTreeDao implements TreeDao
{
    private Map<Integer, Tree> treesById;
    private Collection<Tree> unmodifiableAllTrees;

    public PlainTextFileTreeDao(File treeDataFile)
    {
        this.treesById = new HashMap<Integer, Tree>();
        loadTreesFromFile(treeDataFile);
        this.unmodifiableAllTrees = Collections.unmodifiableCollection(
            treesById.values());
    }

    private final void loadTreesFromFile(File treeDataFile)
    {
        Scanner in = new Scanner(treeDataFile);
        // Pseudocode:
        // While (there are trees left)
        //      tree <- createTree()
        //      put tree in the Map
        // End While
    }

    private final Tree createTree(Scanner in)
    {
        // Write your code to create a single Tree object here.
        // ...
    }

    // Here the contract of TreeDao is implemented.
    @Override
    public Tree getById(int id)
    {
        // ...
    }

    // Here the contract of TreeDao is implemented.
    @Override
    public Collection<Tree> getAll()
    {
        return unmodifiableAllTrees;
    }
}

How to interact with the TreeDao?

// change this according to where your datafile is located
File dataFilePath = new File("C:\\treesdata.dat");
TreeDao treeDao = new PlainTextFileTreeDao(dataFilePath);

// Get the Tree with id 25
Tree tree = treeDao.getById(25); // returns null if there is no Tree with id 25
if (tree != null) tree.paint(g); // assuming we have a Graphics reference here [1]

// In case you want to get all trees at once:
Collection<Tree> trees = treeDao.getAll();

[1] The statement tree.paint(g); is putting Polymorphism to work, we don't need to care what kind of tree we actually got, we just ask that tree to paint itself. If that tree is an AppleTree, then AppleTree's paint method will be called, if it is a LemonTree, then LemonTree's paint method will be called.

P.S. Haven't compiled any of these snippets, since first they are incomplete, so you'll need to fill the gaps first, and second because I'm fairly confident about the code I wrote (although you might need to add some things here and there). Third, since it is not meant to be copy-pasted directly without adjusting it a bit first, it is more to give you an idea.

Thank you, that was really helpful.

Oh my god I'm really sorry but I still have a question about this.
So I made this:

private final void loadTreesFromFile(File treeDataFile)
{
    Scanner in = new Scanner(treeDataFile);

    while(in.hasNext())
    {
        String s = in.next();

        if(s.equals("appleTree"))
        {
            Tree appleTree = new AppleTree();
            appleTree = createTree(in);
            treesById.put(appleTree.getId(),appleTree);
        }

        // And doing the same thing here with all the possible types of trees
    }
}

private final Tree createTree(Scanner in)
{
    Tree tree = new Tree();
    tree.setId(sc.nextInt());
    tree.setX(sc.nextInt());
    tree.setY(sc.nextInt());
    tree.setColor(sc.Next());

    return tree;
}

So I'm really concerned about this part. When I put a tree in the map, it will put a Tree type in, not an AppleTree type or LemonTree type, probably because the returned "tree" in my createTree method is Tree type.
When I use

tree.paint(g);

elsewhere, it will call the Tree class' paint() method, obviously, because my map is filled with that.
And I thought maybe the createTree method is where I should decide whether it's an apple tree or something else but then how do I return that? If I do

Tree appleTree = new AppleTree();

within an if statement, then I can't return that. Also, if I put the typeName as a String in my Tree class and use a set method to set its value to "s" when reading the file, it will be "null" when I'll try to get it in my createTree method.
I'm really sorry for asking these dumb questions, but this is my first try with Java and OOP and I'm literally struggling with this task.

Let's take a look at createTree():

private final Tree createTree(Scanner in)
{
    Tree tree = new Tree(); // don't do this [1]
    tree.setId(sc.nextInt());
    tree.setX(sc.nextInt());
    tree.setY(sc.nextInt());
    tree.setColor(sc.Next());

    return tree;
}

[1] Don't create an instance of the superclass here. We all know how an AppleTree, LemonTree looks. But how is a general Tree supposed to look? When you think about it, it doesn't make alot of sense to be able to construct an object from class Tree at all. You can in fact protect yourself (and others) from instantiating an object from Tree by marking your Tree class as abstract, this means that writing new Tree(); will generate a compiler error. It is very simple to mark a class as abstract, you only have to put the word abstract in front of the class keyword. E.g. public class Tree becomes public abstract class Tree.

And I thought maybe the createTree method is where I should decide whether it's an apple tree or something else

Yup, you should do that.

but then how do I return that?

Declare a Tree reference outside your if block:

// Pseudocode / Skeleton
Tree tree = null;

if (appleTree)
{
    tree = new AppleTree();
    // if there are things specific to an apple tree,
    // then you should set them here
}
else if (lemonTree)
{
    tree = new LemonTree();
    // if there are things specific to a lemon tree,
    // then you should set them here
}

// remark: if tree is still null at this point there's an error in the input file
if (tree != null)
{
    // set the fields which are common to all trees here
    // (location, id...)
    // ...
}

return tree; // outside all if blocks, the tree is returned (or null on error)

I'm really sorry for asking these dumb questions, but this is my first try with Java and OOP and I'm literally struggling with this task.

The only way I know to learn programming is by struggling, so you are not any different from any of us here. We've all struggled in the past (and we all struggle occasionally, or well... at least... I do :P). If you've any more questions, then ask so, asking questions to gain a better understanding of a topic is far from "dumb".

Good luck with your task ;-)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.