I recently saw a topic of some Uni coursework which was being conducted by a friend whom was instructed to do it a certain way. I thought I'd take the opportunity to jump in on the task.
I created a Book class like so:
class Book
{
private String author, title;
public Book setAuthor(String a)
{
author = a;
return this;
}
public Book setTitle(String t)
{
title = t;
return this;
}
public String getAuthor()
{
return author;
}
public String getTitle()
{
return title;
}
}
The concept is that a user can create multiple books at the start of the program and then search for an author:
private final static int BOOK_NO = 3;
private final static SO instance = new SO(); // This is whatever you called the class
public static void main(String[] args)
{
Book[] books = new Book[BOOK_NO];
Scanner kybd = new Scanner(System.in);
for(int i = 0; i < BOOK_NO; i++)
{
books[i] = instance.addBook(kybd, new Book());
}
Arrays.stream(instance.findBook(kybd, books)).forEach(o -> {
System.out.println(o.getTitle() + " by " + o.getAuthor());
});
}
public Book addBook(Scanner s, Book b)
{
System.out.println("Enter the Author of this book:");
b.setAuthor(s.next());
System.out.println("Enter the Title of this book:");
b.setTitle(s.next());
return b;
}
public Book[] findBook(Scanner s, Book[] bs)
{
System.out.println("Search a book by author:");
List<Book> finding = Arrays .stream(bs)
.filter(o -> o.getAuthor().equalsIgnoreCase(s.next()))
.collect(Collectors.toList());
System.out.println("Found " + finding.size() + " matches.");
Book[] output = new Book[finding.size()];
output = finding.toArray(output);
return output;
}
Now the whole program works fine, however I am experience unexpected behaviour with the Scanner when it comes to searching for a book. Here is a direct input/output behaviour I am experiencing:
Enter the Author of this book:
Foo
Enter the Title of this book:
Bar
Enter the Author of this book:
Foo
Enter the Title of this book:
FooBar
Enter the Author of this book:
Bar
Enter the Title of this book:
Foo
Search a book by author:
Foo
Foo
Foo
Found 2 matches.
Bar by Foo
FooBar by Foo
As you can see, I am having to type the author of the book into the scanner 3 times before getting any result. How can I mitigate this? What is causing this to happen?
This is because in your Stream you call next(), so for every Book object in the Stream, the Predicate in the call to filter is applied to it, and next() will be called. Resolve it to a variable so it isn't called more than once:
String book = s.next();
List<Book> finding = Arrays.stream(bs)
.filter(o -> o.getAuthor().equalsIgnoreCase(book))
.collect(Collectors.toList());
filter() accepts a Predicate, which in this case will be something like:
Predicate<String> pred = str -> str.equalsIgnoreCase(s.next());
So every time it is applied, next() will be called
Related
This is my first exercise with boolean. I need to display the output for car_Type whether it's a national car or imported car. The main class was already compiled successfully but when i run it and type in the input it says "Exception in thread "main" java.util.InputMismatchException". Here's the main class:
import java.util.Scanner;
public class CarApp
{
public static void main(String[] args)
{
//declaration
Scanner input = new Scanner(System.in);
String model, brand;
double price;
boolean type;
Car c = new Car();
System.out.println("Enter a car Model : ");//X70/Starex
model = input.nextLine();
System.out.println("\nEnter a car Brand : ");//Proton/Hyundai
brand = input.nextLine();
System.out.println("\nEnter the car price : RM");//95000.00/170000.00
price = input.nextDouble();
System.out.println("\nEnter the car brand [national/imported] : ");//national/imported car
type = input.nextBoolean();
//output
System.out.println("Car Model : "+model);
}
}
Also, here's the car class if you want to check on the error:
public class Car
{
String car_Model;
String car_Brand;
double car_Price;
boolean car_Type;
public Car()
{
car_Model = " ";
car_Brand = " ";
car_Price = 0.0;
car_Type = true;
}
public Car(String cm, String cb, double cp, boolean ct)
{
car_Model = cm;
car_Brand = cb;
car_Price = cp;
car_Type = ct;
}
void SetCar_Model(String cm){
car_Model = cm;
}
void SetCar_Brand(String cb){
car_Brand = cb;
}
void SetCar_Price(double cp){
car_Price = cp;
}
void SetCar_Type(boolean ct){
car_Type = ct;
}
String GetCar_Model()
{
return car_Model;
}
String GetCar_Brand()
{
return car_Brand;
}
double GetCar_Price()
{
return car_Price;
}
Boolean GetCar_Type()
{
return car_Type;
}
public String toString()
{
return "Car Model : " +car_Model + "\nCar Brand : " +car_Brand + "\nCar Price : " +car_Price + "Car Type : " +car_Type;
}
}
Here's the input:
Enter a car Model :
X70
Enter a car Brand :
Hyundai
Enter the car price : RM
95000.00
Enter the car brand [national/imported] :
national
And after i click enter it shows:
Exception in thread "main" java.util.InputMismatchException
at java.base/java.util.Scanner.throwFor(Scanner.java:939)
at java.base/java.util.Scanner.next(Scanner.java:1594)
at java.base/java.util.Scanner.nextBoolean(Scanner.java:1893)
at CarApp.main(CarApp.java:23)
Thank you for your help!
Java boolean data type, can only take the values true or false.
national can't be parsed as a boolean. You have to parse it yourself according to your logic. You could name the field for example isNational and set it to true if the user entered national and false if imported was entered. In your toString method you can then either return "imported" or "national" according to the boolean of isNational like so
isNational ? "national" : "imported"
this is called a ternary operator.
Also think about what should happen if the user enters neither one of those two choices.
You don't need to declare all your variables at the top of the method and then assign them below. Most of the time, if it's deterministic which value gets assigned you can assign it at the time of declaration.
And lastly: usually Java fields are written in camelCase. Take a look on Java naming conventions here.
I am trying to make a to do list that asks you to enter your tasks one by one then display them in order (as in 1. task1, 2. task 2, 3. task 3 etc). But when it displays the tasks it comes back as "0. null" one time instead of listing any of the tasks entered. Here is the script I am using:
1st class
package todolist;
import java.util.ArrayList;
public class ToDoList1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<ToDoList2> list = new ArrayList<ToDoList2>();
System.out.println("Time to make a digital to-do list!");
ToDoList2 n = new ToDoList2();
list.add(n);
System.out.println(ToDoList2.name + "'s to-do list");
System.out.println(ToDoList2.i + ". " + ToDoList2.l);
for(ToDoList2 enhanced : list)
{
System.out.println(ToDoList2.i + ". " + ToDoList2.m);
}
}
}
2nd class
package todolist;
import java.util.Scanner;
public class ToDoList2 {
public static String name;
public static int i;
public static String l;
public static String m;
{
Scanner s = new Scanner(System.in);
System.out.println("First type your name to identify your list in case you lose it");
name = s.nextLine();
System.out.println("Make sure to type \"end\" when you are finished");
System.out.println("Type in the first item on your to-do list");
String l = s.nextLine();
}
public ToDoList2()
{
Scanner s = new Scanner(System.in);
for(int i = 1; i == i; i++)
{
System.out.println("Type in the next item for your to-do list");
String m = s.nextLine();
if("end".equals(m))
{
break;
}
}
}
}
Your code is not correct. ToDoList2 scanning item list from standard input but not storing it. You should do as follow
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class TodoList {
public static String name;
List<String> tasks;
public TodoList(String name) {
this.name = name;
this.tasks = new ArrayList<>();
}
public void addTask(String task) {
this.tasks.add(task);
}
public String toString() {
int i = 1;
StringBuilder stringBuilder = new StringBuilder();
for (String task : tasks) {
stringBuilder.append(i + ". " + task);
stringBuilder.append("\n");
i++;
}
return stringBuilder.toString();
}
}
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("First type your name to identify your list in case you lose it");
String name = s.nextLine();
System.out.println("Make sure to type \"end\" when you are finished");
System.out.println("Type in the first item on your to-do list");
TodoList todoList = new TodoList(name);
String task = null;
while (!(task = s.nextLine()).equals("end")) {
todoList.addTask(task);
System.out.println("Type in the next item for your to-do list");
}
System.out.println(todoList);
}
}
a) Given that each ToDoList2 object is a separate task, I'm not sure why you've made the object class members static?
b) In your ToDoList2 constructor, you've got a for loop that introduces a local variable i which hides the ToDoList2 member variable i. You'd do well to change one of the variable names.
c) In your ToDoList2 constructor, you've got a for loop which is assigning a string returned by the Scanner to a local variable m. Are you sure you want m to be a local variable or do you actually want to assign the returned string to the member variable, m? I'm thinking the latter since the member variable m is never being assigned a value which explains why the code is printing out null.
d) When writing code, it is good practice to use meaningful variable names. Using names like i is OK as an index in a loop but in all other circumstances, you should go for something more descriptive that tells the reader what the variable is storing.
e) Consider making all your ToDoList2 member variables private (and final if possible). Add a print function to the ToDoList2 class to print out the task details. A key principle is Object Oriented Programming is to hide the internals of a class.
I have two classes and they both form ArrayLists. I have a menu option where the user needs to be able to add a book (title, copyright, ISBN, edition, and AUTHOR). This works entirely, but the problem is I need an option to add multiple authors and I cannot think of a way around this. Here is my working code below for adding a single author:
public void addBook(ArrayList<Book> books, ArrayList<Author> aut) {
Scanner scan = new Scanner(System.in);
System.out.println("Please enter the title:");
String title = scan.nextLine();
System.out.println("Please enter the copyright:");
int copy = scan.nextInt();
System.out.println("Please enter the edition:");
int ed = scan.nextInt();
System.out.println("Please enter the isbn:");
String isbn = scan.nextLine();
scan.nextLine();
System.out.println("Please enter the author: ");
String authors = scan.nextLine();
ArrayList<Author> aut = new ArrayList<>();
String firstName = authors.split(" ")[0];
String lastName = authors.split(" ")[1];
Author bookAuthor = new Author(firstName, lastName);
boolean foundAuthor = false;
for (int i = 0; i < aut.size(); i++) {
Author currentAuthor = aut.get(i);
if (currentAuthor.getFirst().equalsIgnoreCase(firstName)
&& currentAuthor.getLast().equalsIgnoreCase(lastName)) {
bookAuthor = currentAuthor;
foundAuthor = true;
break;
}
}
if (!foundAuthor) {
aut.add(bookAuthor);
}
au.add(bookAuthor);
books.add(new Book(title, copy, ed, isbn, au));
}
What is best approach to support the user entering multiple authors?
This will be fairly long, but hopefully this should help you.
First off, you should put a method in your Book class, that will prompt a user for authors, and add any of entered authors into the list for that object.
Here is what that class would look like along with a main that is just for testing purposes. I have excluded all of your other fields:
public class Book {
ArrayList<Author> authors = new ArrayList<>();
public Book() {
}
public Book(String title, int copyright, int edition, String isbn, ArrayList<Author> authors) {
/*
I left these fields out of this example on purpose.
this.title = title;
this.copyright = copyright;
this.edition = edition;
this.isbn = isbn; */
this.authors = authors;
}
public void promptForAuthors()
{
Scanner scan = new Scanner(System.in);
String response = "Y";
do {
System.out.println("Please enter the author. ");
String input = scan.nextLine();
String [] splitAuthors = input.split(" ");
Author author = new Author(splitAuthors[0], splitAuthors[1]);
if (!this.authors.contains(author)) {
this.authors.add(author);
}
System.out.println("Would you like you add an additional author? (Y or N) ");
response = scan.nextLine();
} while(response.equalsIgnoreCase("Y"));
}
public void printAuthors() {
for (Author a : this.authors) {
System.out.println(a.getFirstName() + " " + a.getLastName());
}
}
public static void main(String[] args) {
Book b = new Book(); //This will normally be the constructor that includes the ISBN, copyright, etc.
b.promptForAuthors();
b.printAuthors();
}
}
You can see from the main how you should use this in your addBooks code. Basically you create your Book out of all the other fields you used as input, then you can call this method on that object.
You will probably need to modify your current constructor so that it no longer requires the list for authors to be input when constructed, so you can use it afterward (you can just have both constructors available as well).
The promptForAuthors() will continue to prompt the user to enter in an Author until they hit anything other than Y or y.
This method will always ask for an author as least a single time, due to the use of a do-while loop. One of the main advantages of doing it in the Book class itself, is this method could be reused at any time to add more authors to the same book down the line if that is functionality eventually needed.
Additionally, the method will use .contains() to check if the Author already exists inside of the list, and only add the Author if it is a new Author. This requires an Override of .hashCode() and .equals() in your Author class which you can find the one I made below:
//IN THE AUTHOR CLASS PLACE THESE METHODS AND MODIFY FOR EXTRA FIELDS
#Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Author)) {
return false;
}
Author user = (Author) o;
return user.firstName.equals(this.firstName) &&
user.lastName.equals(this.lastName);
}
#Override
public int hashCode() {
int result = 17;
result = 31 * result + this.firstName.hashCode();
result = 31 * result + this.lastName.hashCode();
return result;
}
Note: I included a printAuthors() method just as an easy way to verify no extra duplicate Authors were added.
Example Run of adding Authors:
Please enter the author.
Joe Smith
Would you like you add an additional author? (Y or N)
y
Please enter the author.
Billy Bob
Would you like you add an additional author? (Y or N)
n
Joe Smith
Billy Bob
Try allowing the user to enter a special string to exit the input loop.
Change
String authors = scan.nextLine();
into a loop to populate a list of strings from input
ArrayList<String> authorNames = new ArrayList<>();
System.out.println("Please enter authors. Enter DONE when you are done.");
String nextLine = scan.nextLine();
while (!nextLine.equals("DONE")) {
authorNames.add(nextLine);
nextLine = scan.nextLine();
}
// This doesn't prevent the user from entering zero authors. Idk if you care about that.
alternatively, accept input of authors as only one line with some delimiter and use String.split on that delimiter later.
1 - create a list of lists.(List of Another list author).
2 - create an object of author list.
3 - perform all the action you want to perform on this author list.
4 - add author list to list of author list.
5 - To access author you need syntax like first_list_object.get(index).author_object_variable_or_member.
ArrayList<ArrayList<Author>> authorList = new ArrayList<>();
ArrayList<Author> au = new ArrayList<Author>();
String firstName = authors.split(" ")[0];
String lastName = authors.split(" ")[1];
Author bookAuthor = new Author(firstName, lastName);
au.add(bookAuthor);
authorList.add(au);
I think You're working with the wrong collection. You have a couple of author's. Each author could have written several books. So the right thing would be a Map<Author,ArrayList<Book>>.
It will make Your programming logic for addBook() easier, if You can use a map.
Requirements: accommodate multiple authors using one of the components from the Java Collection Framework. Requires one book with an isbn and a Collection of authors. JUnit: Guidance for testValidate: Test for at least two cases (one case where the book properties hold the correct data types and are not empty nor hold a null value, one where they do not). Guidance for testEquals: Test for at least two cases (one case where authors and isbn match, one where they do not). Test for at least two authors. My teacher told me: testEquals you need to add isbn and two authors. Create an ArrayList. Add two authors to it. Create a Book object and add the ArrayList instance and the isbn. I think that's what I have done, the authors are printing, but the ISBNs are not. I am a total newbie and I am at a loss! Can anyone help?
EDIT/ADDITION I got the ISBN to print, but it is only printing the second isbn I have. What do I need to change to get both of them to print? Or does it matter?
Here is the output:
Testsuite: library.domain.BookTest
equals
Author List: [Bob Smith, Jane Doe]
ISBN: 67890
validate
Author List: [Bob Smith, Jane Doe]
ISBN: 67890
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.23 sec
------------- Standard Output ---------------
equals
Author List: [Bob Smith, Jane Doe]
ISBN: 67890
validate
Author List: [Bob Smith, Jane Doe]
ISBN: 67890
------------- ---------------- ---------------
test:
Deleting: /var/folders/k7/wpgy3lw91171qxlzt4pj0cfh0000gn/T/TEST-library.domain.BookTest.xml
BUILD SUCCESSFUL (total time: 1 second)
Here is my new page:
NEW BookTest.java
package library.domain;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class BookTest {
private ArrayList<String> authorList = new ArrayList<>();
#Test
public void testEquals() //test Equals() for accuracy
{
System.out.println("equals");
authorList.add("Bob Smith");
Book book = new Book("12345", authorList);
assertEquals("expected true", true, book.equals(book));
authorList.add("Jane Doe");
book = new Book("67890", authorList);
assertEquals("expected true", true, book.equals(book));
System.out.println("Author List: " + authorList);
System.out.println("ISBN: " + book.getIsbn());
}
#Test
public void testValidate() //test Validate() for accuracy
{
System.out.println("validate");
authorList.add("Bob Smith");
Book book = new Book("12345", authorList);
assertEquals("expected true", true, book.validate());
authorList.add("Jane Doe");
book = new Book("67890", authorList);
assertEquals("expected true", true, book.validate());
System.out.println("Author List: " + authorList);
System.out.println("ISBN: " + book.getIsbn());
}
}
Book.java
package library.domain;
import java.util.ArrayList;
import java.util.Objects;
public class Book {
private String isbn;
private ArrayList<String> authorList;
public Book(String isbn, ArrayList<String> authorList)
{
this.isbn = isbn;
this.authorList = authorList;
}
public String getIsbn() //access to isbn and manages next value
{
return isbn;
}
public void setIsbn(String isbn) //assigns the input isbn to the data member isbn
{
this.isbn = isbn;
}
//assigns the input author to the data member author
public ArrayList<String> getAuthorList()
{
return authorList;
}
public void setAuthorList(ArrayList<String> authorList)
{
this.authorList = authorList;
}
#Override
public boolean equals(Object obj) //checks equality of two objects - true if same, false if different
{
if (this == obj) {
return true;
}
if (!(obj instanceof Book)) {
return false;
}
Book book = (Book) obj;
if (!this.isbn.equals(book.isbn)) {
return false;
}
if (!this.authorList.equals(book.authorList)) {
return false;
}
return true;
}
#Override
public int hashCode() //override hash
{
int hash = 7;
hash = 97 * hash + Objects.hashCode(this.authorList);
hash = 97 * hash + Objects.hashCode(this.isbn);
return hash;
}
public boolean validate() //validate isbn and author not null
{
if (isbn == null || isbn.equals("")) {
return false;
}
if (authorList == null || authorList.equals("")) {
return false;
}
{
return true;
}
}
}
BookTest.java
package library.domain;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class BookTest {
private ArrayList<String> authorList = new ArrayList<>();
private String isbn;
#Test
public void testEquals() //test Equals() for accuracy
{
System.out.println("equals");
authorList.add("Bob Smith");
authorList.add("Jane Doe");
Book book = new Book("12345", authorList);
assertEquals("expected true", true, book.equals(book));
System.out.println("Author List: " + authorList);
System.out.println("ISBN: " + isbn);
}
#Test
public void testValidate() //test Validate() for accuracy
{
System.out.println("validate");
authorList.add("Bob Smith");
authorList.add("Jane Doe");
Book book = new Book("12345", authorList);
assertEquals("expected true", true, book.validate());
System.out.println("Author List: " + authorList);
System.out.println("ISBN: " + isbn);
}
}
isbn in the test class is a local variable and you are not setting any values for the same. To check if object is being created correctly, try printing book.getAuthorList() and book.getIsbn()
In BookTest you are printing out the value of the isbn in the scope of BookTest. You initialized it in BookTest so any calls to isbn will refer to the global variable
private String isbn;
You need to print out the isbn field of the Book object.
System.out.println("ISBN: " + book.getIsbn());
You had the getter method written, you just forgot to use it.
When accessing the ISBN and authorList you should use their getter methods. In your test there is no String object named ISBN. Instead use book.getIsbn();
Some other things:
In the validate() method; authorList is a List not a String so authorList.equals("") will never return true. You should do something like authorList.size() == 0. Also the return true doesn't need to be within {}.
Also you shouldn't just try to print a list (System.out.println("Author List: " + authorList);). You could get very unexpected results.
Book book = new Book("12345", authorList);
Here you are setting value to isbn is available in Book class.
System.out.println("ISBN: " + isbn);
But here you are trying print isbn which is present in current class. So it doesn't print any value. Since you are not pass any value to it.
To print value of isbn, available in Book class write
System.out.println("ISBN: " + book.getIsbn());
System.out.println("Author List: " + authorList);
Here also authorList is a local variable. To print value of authorList is available in Book class wirte
System.out.println("Author List: " + book.authorList());
I'm working on a type of inventory program that deals with reading in a list of items from a file and I'm having some trouble.
Here's the code I have finished so far:
import java.util.Scanner;
import java.io.*;
public class Store {
public static void main(String[] args) throws Exception {
Book[] books = readInventory();
for (Book book : books) {
System.out.printf("ISBN: %s, Price: %f, Copies: %d%n",
book.getISBN(), book.getPrice(), book.getCopies());
}
}
public static Book[] readInventory() throws Exception {
Book[] books = new Book[15];
java.io.File file = new java.io.File("../instr/prog4.dat");
Scanner fin = new Scanner(file);
String isbn;
double price;
int copies;
int i = 0;
while (fin.hasNext()) {
isbn = fin.next();
if (fin.hasNextDouble()); {
price = fin.nextDouble();
}
if (fin.hasNextInt()); {
copies = fin.nextInt();
}
Book book = new Book(isbn, price, copies);
books[i] = book;
i++;
}
fin.close();
return books;
}
public static void printInfo(Book[] books) {
for(int x=0; x<books.length; x++) {
System.out.println("ISBN: " + books[x].getISBN() + "\n Price: " +
books[x].getPrice() + "\n Copies: " + books[x].getCopies());
}
}
}
class Book {
private String isbn;
private double price;
private int copies;
public Book(String isbnNum, double priceOfBook, int copiesInStock) {
isbn = isbnNum;
price = priceOfBook;
copies = copiesInStock;
}
public String getISBN() {
return isbn;
}
public double getPrice() {
return price;
}
public int getCopies() {
return copies;
}
public void setISBN(String isbn) {
this.isbn = isbn;
}
public void setPrice(double price) {
this.price = price;
}
public void setCopies(int copies) {
this.copies = copies;
}
#Override
public String toString() {
return String.format("ISBN: %s, Price: %f, Copies: %d%n",
this.getISBN(), this.getPrice(), this.getCopies());
}
}
The program compiles fine, but when I run the program I get the error
Exception in thread "main" java.util.NoSuchElementException
at java.util.Scanner.throwFor(Scanner.java:855)
at java.util.Scanner.next(Scanner.java:1364)
at Store.readInventory(Store.java:31)
at Store.main(Store.java:13)
Here is the contents of the file I am supposed to use:
1234567 31.67 0
1234444 98.50 4
1235555 27.89 2
1235566 102.39 6
1240000 75.65 4
1247761 19.95 12
1248898 155.91 0
1356114 6.95 17
1698304 45.95 3
281982X 31.90 5
I have been looking at another program I wrote recently that dealt with reading files to see that I was throwing the exceptions in the same types of places and it appears that I am (everything dealing with the file in that program was in the main method so that was the only one throwing an exception). From what I remember from an earlier Java programming class, we were supposed to throw exceptions at the main method and any other methods that deal with the file, so in this case I have the one on main and another on my readInventory() method.
What am I missing?
Check for next value before reading to avoid such exception as you are doing for isbn but not for price and copies.
sample code:
if(fin.hasNextDouble()){
price = fin.nextDouble();
}
if(fin.hashNextInt()){
copies = fin.nextInt();
}
For example Scanner#nextInt() method throws:
InputMismatchException - if the next token does not match the Integer regular expression, or is out of range
NoSuchElementException - if input is exhausted
IllegalStateException - if this scanner is closed
You are creating array of size 15 where is there is only 10 line in file that result in NullPointerException. Use List in that case.
You can use Java 7 - The try-with-resources Statement for better resource management even in case of error.
sample code
public static BookDetail[] readInventory() throws Exception {
List<BookDetail> books = new ArrayList<BookDetail>();
java.io.File file = new java.io.File("../instr/prog4.dat");
try (Scanner fin = new Scanner(file)) {
String isbn;
double price = 0;
int copies = 0;
while (fin.hasNext()) {
isbn = fin.next();
if (fin.hasNextDouble()) {
price = fin.nextDouble();
}
if (fin.hasNextInt()) {
copies = fin.nextInt();
}
BookDetail book = new BookDetail(isbn, price, copies);
books.add(book);
}
}
return books.toArray(new BookDetail[books.size()]);
}
Currently, you are checking if the file has more data, then you attempt to read 15 books, no matter what. Your for-loop goes from 0 to books.length-1, whatever is in the file.
Since you don't know (in theory) how many books there are in the file, you need to check fin.hasNext before reading each book.
So get rid of your for-loop and replace your reading code with something like this:
int i = 0;
while (fin.hasNext()) {
// read book here and assign to books[i]
// increment i for next iteration
i++;
}
The while condition fin.hasNext() will be checked before reading each book. When it returns false, you'll stop looping, and i will be the number of books you read.
Note that you're still assuming that each book has all its fields. If there was only half a book in the file, your code would still crash. But that's probably a good thing, since there's not really a way you can recover from that, and presumably you can assume you will receive only well-formed input.
I would suggest you use exception handling. I especially recommend using try with resources, since you forget to close the scanner that you are using.
You need to check in the code that you receive all the information that you need for each book. You don't want to have only partial information on the book just because you couldn't receive the rest of the data on that particular book.