简体   繁体   中英

Java - Populating List by Building Different Types of Objects with Data from File

Hi guys hope you're well (-:

I'm trying to write a method that reads from a file and creates different types of objects (Academic, Student, Programmer) that it then adds to a list (Person). I'm stuck trying to figure out a way to create different objects for each instance of a type of person, and add them to the list in the order that they are read. As in, it might go Programmer, Programmer, Student, Programmer, and it should add them in that order to the list. I've gotten it working where it creates an object for each type, but this means the output of the list is grouped by type instead of read order.

The data it reads will be newline separated, with the following format:

<type of person>
<name>
<additional data>

Where the type of person will determine which subclass of Person to create, name is the person's name, and additional data is a series of lines of additional of information corresponding to the type of person. A student will have (in this order): the name of their degree, which year they're in. An academic will have what subject they teach. A programmer will have their favourite programming language.

input file:

Student
Alice
Computer Science
2
Academic
Bob
Programming
Programmer
Eve
Scala
Student
Jim
Computer Science
1

So at the moment the output of this would be

[Hi, I'm Alice. I am studying Computer Science. I am in my 2nd year., Hi, I'm Jim. I am studying Computer Science. I am in my 1st year., Hi, I'm Bob. I teach Programming., Hi, I'm Eve. I like to program in Scala.]

But I want it to be

[Hi, I'm Alice. I am studying Computer Science. I am in my 2nd year., Hi, I'm Bob. I teach Programming., Hi, I'm Eve. I like to program in Scala., Hi, I'm Jim. I am studying Computer Science. I am in my 1st year.]

And here is the code-

import java.io.*;
import java.util.*;

public class MiniFB {

      public static List<Person> readInData(String filename) {
//this is the method I am talking about
        ArrayList<Person> list = new ArrayList<Person>();
         
          try (Scanner sc = new Scanner(new File(filename))) {
            while (sc.hasNextLine()) {
               if (sc.nextLine().equals("Student")) {
                      String n = sc.nextLine();
                     String d = sc.nextLine();
                       int y = sc.nextInt();
                       Student s = new Student(n, d, y);
                       list.add(s);
                  }
            }
            sc.close();
        }
        catch (IOException e) {
            System.err.println("error");
        }
//
try (Scanner sc = new Scanner(new File(filename))) {
            while (sc.hasNextLine()) {
                if (sc.nextLine().equals("Academic")) {
                     String n = sc.nextLine();
                     String s = sc.nextLine();
                    Academic a = new Academic(n, s);
                       list.add(a);
                  }
            }
            sc.close();
        }
        catch (IOException e) {
            System.err.println("error");
        }
//
try (Scanner sc = new Scanner(new File(filename))) {
            while (sc.hasNextLine()) {
                if (sc.nextLine().equals("Programmer")) {
                     String n = sc.nextLine();
                     String l = sc.nextLine();
                    Programmer p = new Programmer(n, l);
                       list.add(p);
                  }
            }
            sc.close();
        }
        catch (IOException e) {
            System.err.println("error");
        }
//        
        return list;
    }

    public static void main(String[] args) {
        List<Person> people = readInData("testFile.txt");

        System.out.println(people);
    }

}

public abstract class Person {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    @Override
    public String toString() {
        return "Hi, I'm " + name + ".";
    }

    @Override
    public abstract boolean equals(Object other);
}

public class Student extends Person {

    private String degree;
    private int year;
    
    public Student(String name, String degree, int year) {
        super(name);
        this.degree = degree;
        this.year = year;
    }
    
    private String ordinalEnding(int cardinal) {
        
        if (cardinal % 100 >= 11 && cardinal % 100 <= 13) return "th";
        
        switch (cardinal % 10) {
            case 1 : 
                return "st";
            case 2 : 
                return "nd";
            case 3 : 
                return "rd";
            default : 
                return "th";
        }
    }
    
    @Override
    public String toString() {
        return super.toString() + " I am studying " + this.degree + ". I am in my " + this.year + ordinalEnding(this.year) + " year.";
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof Student)) return false;
        Student other = (Student)o;
        return (this.getName().equals(other.getName()) && this.degree.equals(other.degree) && this.year == other.year);
    }

}

public class Academic extends Person {
    private String subject;
    
    public Academic(String name, String subject) {
        super(name);
        this.subject = subject;
    }
    
    @Override
    public String toString() {
        return super.toString() + " I teach " + this.subject + ".";
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof Academic)) return false;
        Academic other = (Academic)o;
        return (this.getName().equals(other.getName()) && this.subject.equals(other.subject));
    }
}

public class Programmer extends Person {
    
    private String language;
    
    public Programmer(String name, String language) {
        super(name);
        this.language = language;
    }
    
    @Override
    public String toString() {
        return super.toString() + " I like to program in " + this.language + ".";
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof Programmer)) return false;
        Programmer other = (Programmer)o;
        return (this.getName().equals(other.getName()) && this.language.equals(other.language));
    }
}

Okay, so the basic problem is with how you are parsing the file...

try (Scanner sc = new Scanner(new File(filename))) {
    while (sc.hasNextLine()) {
       if (sc.nextLine().equals("Student")) {
            System.out.println("Student");
            String n = sc.nextLine();
            String d = sc.nextLine();
            int y = sc.nextInt();
            Student s = new Student(n, d, y);
            list.add(s);
        }
    }
    sc.close();
}
catch (IOException e) {
    System.err.println("error");
}
try (Scanner sc = new Scanner(new File(filename))) {
    while (sc.hasNextLine()) {
        if (sc.nextLine().equals("Academic")) {
            System.out.println("Academic");
            String n = sc.nextLine();
            String s = sc.nextLine();
            Academic a = new Academic(n, s);
            list.add(a);
        }
    }
    sc.close();
}
catch (IOException e) {
    System.err.println("error");
}

try (Scanner sc = new Scanner(new File(filename))) {
    while (sc.hasNextLine()) {
        if (sc.nextLine().equals("Programmer")) {
            System.out.println("Programmer");
            String n = sc.nextLine();
            String l = sc.nextLine();
            Programmer p = new Programmer(n, l);
           list.add(p);
        }
    }
    sc.close();
}
catch (IOException e) {
    System.err.println("error");
}        

The first scan will pick up both students, then the next will pick up the academics and the last the programmers, which is great if you want to sort them like that.

Opening and closing the file like this is inefficient, a better solution would be to read each line and make an assessment over what you need to do with it, something like...

try ( Scanner sc = new Scanner(new File(filename))) {
    while (sc.hasNextLine()) {
        String line = sc.nextLine();
        if (line.equals("Student")) {
            String n = sc.nextLine();
            String d = sc.nextLine();
            int y = sc.nextInt();
            Student s = new Student(n, d, y);
            list.add(s);
        } else if (line.equals("Academic")) {
            String n = sc.nextLine();
            String s = sc.nextLine();
            Academic a = new Academic(n, s);
            list.add(a);
        } else if (line.equals("Programmer")) {
            String n = sc.nextLine();
            String l = sc.nextLine();
            Programmer p = new Programmer(n, l);
            list.add(p);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

This will load each entry in the order it appears in the file and will print

[Hi, I'm Alice. I am studying Computer Science. I am in my 2nd year., Hi, I'm Jim. I am studying Computer Science. I am in my 1st year., Hi, I'm Bob. I teach Programming., Hi, I'm Eve. I like to program in Scala.]

Another thing you might want to consider, is allowing each class to parse the text from the Scanner itself, as each class would have a better idea of what it needs and it helps decouple some of the logic, as a suggestion.

So, we could modify the Person and child classes to accept a Scanner via the constructor, this means that each class becomes responsible for it's own parsing...

public static abstract class Person {

    private String name;

    public Person(Scanner scanner) {
        this.name = scanner.nextLine();
    }

    public String getName() {
        return this.name;
    }

    @Override
    public String toString() {
        return "Hi, I'm " + name + ".";
    }

    @Override
    public abstract boolean equals(Object other);
}

public static class Student extends Person {

    private String degree;
    private int year;

    public Student(Scanner scanner) {
        super(scanner);
        this.degree = scanner.nextLine();
        this.year = scanner.nextInt();
    }

    private String ordinalEnding(int cardinal) {

        if (cardinal % 100 >= 11 && cardinal % 100 <= 13) return "th";

        switch (cardinal % 10) {
            case 1 : 
                return "st";
            case 2 : 
                return "nd";
            case 3 : 
                return "rd";
            default : 
                return "th";
        }
    }

    @Override
    public String toString() {
        return super.toString() + " I am studying " + this.degree + ". I am in my " + this.year + ordinalEnding(this.year) + " year.";
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof Student)) return false;
        Student other = (Student)o;
        return (this.getName().equals(other.getName()) && this.degree.equals(other.degree) && this.year == other.year);
    }

}

public static class Academic extends Person {
    private String subject;

    public Academic(Scanner scanner) {
        super(scanner);
        this.subject = scanner.nextLine();
    }

    @Override
    public String toString() {
        return super.toString() + " I teach " + this.subject + ".";
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof Academic)) return false;
        Academic other = (Academic)o;
        return (this.getName().equals(other.getName()) && this.subject.equals(other.subject));
    }
}

public static class Programmer extends Person {

    private String language;

    public Programmer(Scanner scanner) {
        super(scanner);
        this.language = scanner.nextLine();
    }

    @Override
    public String toString() {
        return super.toString() + " I like to program in " + this.language + ".";
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) return false;
        if (!(o instanceof Programmer)) return false;
        Programmer other = (Programmer)o;
        return (this.getName().equals(other.getName()) && this.language.equals(other.language));
    }
}

Then reading the file becomes as simple as...

try ( Scanner sc = new Scanner(new File(filename))) {
    while (sc.hasNextLine()) {
        String line = sc.nextLine();
        if (line.equals("Student")) {
            Student s = new Student(sc);
            list.add(s);
        } else if (line.equals("Academic")) {
            Academic a = new Academic(sc);
            list.add(a);
        } else if (line.equals("Programmer")) {
            Programmer p = new Programmer(sc);
            list.add(p);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

We could talk a lot about "factory patterns" and the like, but this gets the basic idea across;)

No, you don't need to do this, it's just another way to approach the problem;)

ArrayList<Person> list = new ArrayList<Person>();
try (Scanner sc = new Scanner(new File(filename))) {
  while (sc.hasNextLine()) {
    String line = sc.nextLine();
    if (line.equals("Student")) {
       String n = sc.nextLine();
       String d = sc.nextLine();
       int y = sc.nextInt();
       Student s = new Student(n, d, y);
       list.add(s);
    } else if (line.equals("Academic")) {
       String n = sc.nextLine();
       String s = sc.nextLine();
       Academic a = new Academic(n, s);
       list.add(a);
    } else if (line.equals(...)) {
      ..
    }
  }
  sc.close();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM