I have the following program:
class Books
{
String title;
String author;
}
class BookTestDrive
{
public static void main(String [] args)
{
Books [] myBooks = new Books[3];
int x = 0;
myBooks[0].title = "The Grapes of Java";
myBooks[1].title = "The Java Gatsby";
myBooks[2].title = "The Java Cookbook";
myBooks[0].author = "bob";
myBooks[1].author = "sue";
myBooks[2].author = "ian";
while (x < 3)
{
System.out.print(myBooks[x].title);
System.out.print(" by ");
System.out.println(myBooks[x].author);
x = x + 1;
}
}
}
However, it gives me the following error when I execute it:
Exception in thread "main" java.lang.NullPointerException
at BookTestDrive.main(Books.java:14)
I am new to Java. The code looks legitimate from my C/C++ experience...How to resolve this problem?
The issue is that you have only created the array of books in the following lines -
Books [] myBooks = new Books[3];
You still need to initialize each element in the array to a book object before accessing them.
An example code would look like -
Books [] myBooks = new Books[3];
int x = 0;
myBooks[0] = new Books();
myBooks[0].title = "The Grapes of Java";
You need to do this for all elements in your array.
I second the answer from #AnandSKumar (it is the direct answer to the problem after all), but because it is a matter of beauty, I could not leave without making following few changes:
public class Play {
static public class Book {
final public String title;
final public String author;
public Book(String title,String author) {
this.title = title;
this.author = author;
}
#Override
public String toString() {
return "\""+title+"\" by "+author;
}
}
public static void main(String [] args)
{
Book [] books = new Book[] {
new Book("The Grapes of Java","bob"),
new Book("The Java Gatsby","sue"),
new Book("The Java Cookbook","ian"),
};
for (Book book:books) {
System.out.println(book);
}
}
}
You can initialize in-line the content of your array
If you represent 'a single book' then we should name the class representing it as Book and not Books to avoid confusion.
We can enhance the Book class with an improved toString(), and use that instead.
There is a enhanced for iterator to loop over your array.
Note that the third position book in the array, also ends with a comma, although there is no element following it. This could have been a mistake, but it this case it was a deliberate choice. Makes it easier to copy-paste into next elements without introducing errors, as the Java syntax allows for it.
Because once a book is created, title and author should not change anymore, it might be good to design the Book class to be 'Immutable'. For this reason a constructor was added, and the title and author fields set as final. You could also consider making them private, and provide getters.
Related
I am writing a program to store certain Recipe info. into an array. Using method RecipeEntry(), with its parameters, the program is meant to store up to a maximum of 3 recipes into an array named: totalEntry[].
Below is my attempt at writing this program, however, I am getting errors .. unable to figure out what it is that I am missing.
import java.util.*;
public class RecipeArray
{
private String author;
private Recipe recpH_1;
private Recipe recpC_2;
private static RecipeEntry[] totalEntry = new RecipeEntry[3];
private static int entryCount;
public RecipeArray(String author, Recipe hot, Recipe cold) // constructor
{
entryCount = 0;
this.author = author;
recpH_1 = hot;
recpC_2 = cold;
totalEntry[entryCount] = new RecipeArray(author, recpH_1, recpC_2);
}
public static void main(String[] args)
{
RecipeEntry("Mary Bush", SpaghettiHOT, SpaghettiCOLD);
// RecipeEntry method, when called should pass its parameter values into
// totalEntry [] array. entryCount variable should keep count of every entry.
System.out.println("ALL ENTRY = " + entryCount + Arrays.toString(totalEntry));
}
}
public class Recipe //create class data of type recipe
{
private String name;
private int id, rating;
public Recipe(String name)
{
this.name = name;
id = 0;
rating = 0;
}
}
The expected output should print a list of the entries - Example:
Output:
index 0 - [Mary Bush, SpaghettiHOT{id=0, rating=0}, SpaghettiCOLD{id=0, rating=0}]
The problem is that you didn't catch the returned Array from your method:
RecipeEntry("Mary Bush", SpaghettiHOT, SpaghettiCOLD);
Actually the RecipeArray as you wrote will return an Array; it means that the method will pass and Array to it's caller.
Changing the mentioned line with the following line will solve the problem:
RecipeEntry[] totalEntry = RecipeEntry("Mary Bush", SpaghettiHOT, SpaghettiCOLD);
Visit https://www.tutorialspoint.com/importance-of-return-type-in-java for better understanding of Java Methods:
I couldn't find an answer so I have to post a new question - although it seems to be similiar to few another questions that were asked already here - but to the point:
I have got some list, for example:
private static List<Book> books = new ArrayList<>();
It contains objects of class Book, where I have got
private String title;
private String author;
and getters&setters.
Now I have got a new object Book (got from webform through parameters/servlet) and I want to check if title of new book is already on list books.
If it is already on that list, print some console output. If it is not, I want to add the whole new object to list. In order to do that, I tried to compare titles using regular foreach loops. Sometimes it worked out, sometimes not (sometimes my function cwere comparing two titles in a right way but sometimes not - I don't know why). I know there is a better way of doing this using Java 8. But I'm newbie when it comes to Java 8. Could somebody show me a good way of doing this?
I can't tell why you want to do it specifically with java-8 features, but it could be done like this:
boolean isPresent = books.stream()
.anyMatch(book -> book.getTitle().equals(inputBook.getTitle()));
if(isPresent){
... console print
} else {
add it to books
}
I think that this could be made a bit more generic btw, to have a method that would accept a Predicate.
private boolean exists(List<T> list, Predicate<T> predicate) {
return list.stream().anyMatch(predicate);
}
So that you could match anything you like later:
boolean exists = exists(books, p -> p.getTitle().equals(b.getTitle()));
Or :
Predicate<Book> first = p -> p.getTitle().equals(b.getTitle());
Predicate<Book> second = first.and(p -> p.getTitle().equals(b.getTitle()));
boolean exists = exists(books, second);
You can try with findAny and Optional
Optional<Book> found = books.stream().filter(p -> p.getTitle().equalsIgnoreCase(newBook.getTitle())).findAny();
if (found.isPresent()){
System.out.println(found.get());
}
else {
books.add(newBook);
}
If you wanted to be a little fancy and methodical you could create a 'Library' class that inherits or has-a ArrayList. What you need then is an equals function for book that uses String .equals() on the title. The contains method could then call this looping over the elements.
First you must create the getter's & setter's for the class Book. A demonstration of that would be like this:
class Book{
private String title;
private String author;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Now you can do the following to surf through every objects' title and compare it with the newly entered objects' title.
Scanner in = new Scanner(System.in);
List<Book> books = new ArrayList<>();
Book b = new Book();
System.out.println("Enter the title:");
String t = in.nextLine();
b.setTitle(t); //optional
for (Book book:books
) {
if(book.getTitle().equals(t))
System.out.println("Title exists");
else
System.out.println("Title does not exist");
}
public class Book
{
private String isbn, author, area;
private int length;
public Book(String isbn, String author, String area, int length)
{
this.isbn=isbn;
this.author=author;
this.area=area;
this.length=length;
}
public boolean isLong()
{
if (length>500)
return true;
else
return false;
}
}
public class BookCollection
{
private ArrayList<Book> bookList = new ArrayList<Book>();
public BookCollection()throws IOException
{
Scanner fileScan;
String oneLine;
String isbn, author, area;
int length;
fileScan = new Scanner (new File("books.txt"));
while (fileScan.hasNext())
{
isbn=fileScan.next();
author=fileScan.next();
area=fileScan.next();
length=fileScan.nextInt();
bookList.add(new Book(isbn, author, area, length));
}
}
public class TestBookCollection
{
public static void main (String[] args)throws IOException
{
BookCollection books = new BookCollection();
System.out.println(books);
}
}
All right here is the relevant code. My project is to read a text file that has the information about these books and put them into an arraylist of book objects. My question is: how would I go about envoking the isLong() method found in class Book on an object in an arraylist? The point of the method is, if an object has >500 pages that it returns true. If not it will return false. I'm just kind of confused about the logic and I have never really worked with Arraylists before.
You can add another method to BookCollection:
public void printLongBooks() {
for (Book book : bookList) {
if (book.isLong()) {
well, book is long ...
} else {
obviously, it is short ...
}
The above uses the so called "for each" looping style in Java that you can use to loop every array/collection; instead of the "old school" for (int i=0; i<...) counting loop.
and within your main method, you simply invoke the new method:
books.printLongBooks()
And some generic hints:
A) isLong() can be reduced to a one-liner: return length > 500;
B) reading stuff from a file is not what you directly do in the constructor. Instead, you should create a dedicated method to that, which you then might call within the constructor
Rather than putting the logic to create book list inside a constructor, you should create a method named as getBookCollection() that returns an arraylist.
public List<Book> BookCollection()throws IOException
{
List<Book> bookList = new ArrayList<Book>();
//logic to create booklist
return booklist;
}
Once you have list of books, you can run an enhanced for loop and check for pages in each book.
for(Book book: bookList){
if(book.isLong()){
//logic
} else {
//logic
}
}
You can use a for loop to iterate through the elements of the ArrayList and call the method on each one of its objects.
for(Book book : bookList){
boolean isLong = book.isLong();
}
You have a List<Book>, where each index in the List contains a Book. Use the List.get(index) method to get the Object in the given List at index. For example: bookList.get(0) gets the Book at index 0. Once you have that Object, you can use it normally.
I assume that you have some way to get the bookList inside BookCollection, and the name of a Book?
public static void main(String[] args){
BookCollection books = new BookCollection(); // Make a new BookCollection
List<Book> booksInCollection = books.getBooksList(); // Get the Books inside BookCollection
// Go through each book in booksInCollection
for(int index = 0; index < booksInCollection.size(); index++){
// Get the Book Object at index
Book currentBook = booksInCollection.get(index);
if(currentBook.isLong()){
System.out.println(currentBook.getName() + " is long!");
}
}
}
Firstly (if you don't want to use streams etc.) you need to get object from ArrayList. You can do it by using ArrayList.get(int index) and then invoking the method or by using for each loop:
for(Book book : bookList) {
System.out.println(book.isLong());
}
The problem is that you cannot access private field (bookList) from main. There are several ways to make it work.
Create a public method inside BookCollection
Change field to public and then access it directly books.bookList
Make a getter to your bookList in BookCollection
Make BookCollection extends ArrayList
What you've done here is created a decorator (BookCollection) which wraps an ArrayList with additional functionality (in this case, a constructor which pre-fills it based on a file). This gives you two options for how to make use of a Book in the List:
Make the List accessible via a getter, and then work it out. Makes statements long but this is acceptable for small amounts of usage:
if(books.getBooks().get(index).isLong()) {
//The book at index is long
}
More commonly, you'll make methods in the decorator which offer common functionality, like so:
public boolean isLong(int index) {
return bookList.get(index).isLong();
}
Then you just call the decorator method from your business logic. You can even make more complex methods such as the one offered by GhostCat.
Below Is a Junit test class I have written. I am wondering how it would be possible to test the boundaries of each class.
public class TestLibrarySearch {
// Test data structures - books, etc
// some books
Book b1, b2, b3;
String authorB1, authorB2, authorB3;
String ISBNB1, ISBNB2, ISBNB3;
String titleB1, titleB2, titleB3;
int ratingB1, ratingB2, ratingB3;
// the array list of books structure
ArrayList<Book> arrayList;
#Before
public void setUp() throws Exception {
// create some books with test data
authorB1 = "B1Author";
authorB2 = "B2Author";
authorB3 = "B3Author";
titleB1 = "B1Title";
titleB2 = "B2Title";
titleB3 = "B3Title";
ISBNB1 = "ISBNB1AAAA";
ISBNB2 = "ISBNB2AAAA";
ISBNB3 = "ISBNB3AAAA";
ratingB1 = 1;
ratingB2 = 1;
ratingB3 = 3;
// create the test book object
b1 = new Book(ISBNB1, authorB1, titleB1, ratingB1);
b2 = new Book(ISBNB2, authorB2, titleB2, ratingB2);
b3 = new Book(ISBNB3, authorB3, titleB3, ratingB3);
// create the array list of books
arrayList = new ArrayList<Book>();
// add the book to the list
arrayList.add(b1);
arrayList.add(b2);
arrayList.add(b3);
}
For example in the below class, the method allows for a search of a book using a certain rating. (1-5), how would I implement a test that also searches outside of the limits, Would this be testing for an exception?
#Test
public void testSearchByRating() {
LibrarySearch ls = new LibrarySearch(arrayList);
ArrayList<Book> results = ls.searchByRating(1);
if ((results.contains(b1)) && (results.contains(b2))){
assertTrue(true);
} else {
assertTrue(false);
}
}
}
In my opinion you have too much setup and an assertion that is not very clear.
Having an if statement in a test is a smell.
refactoring out the if statement leaves the test with:
#Test
public void testSearchByRating() {
LibrarySearch ls = new LibrarySearch(arrayList);
ArrayList<Book> results = ls.searchByRating(1);
assertTrue(results.contains(b1) && results.contains(b2));
}
Actually you could include the neccassary setup here so you have the essence of what you are testing at a glance. Additionally beware of magic numbers. you want to be clear that the rating in the book corresponds to the rating you are searching for.
#Test
public void testSearchByRating() {
//setup/given
int rating = 1;
// if the ISBN is a key, specify 2 different strings, if not use a constant
// if the structure of the ISBN is not checked, prefer a String like "isbn 1"
Book b1 = new Book("ISBNB1AAAA", ANY_AUTHOR, ANY_TITLE, rating);
Book b2 = new Book("ISBNB2AAAA", ANY_AUTHOR, ANY_TITLE, rating);
LibrarySearch ls = new LibrarySearch(Arrays.asList(b1,b2));
// execution/when
ArrayList<Book> results = ls.searchByRating(rating);
// assertion/then
assertTrue(results.contains(b1));
assertTrue(results.contains(b2));
}
If you want to test for searches with an illegal rating, consider the desired behaviour.
This could be one of the following:
#Test(expected=IllegalArgumentException.class)
public void searchForNegativeRatingThrows() {
LibrarySearch ls = new LibrarySearch(Collections.emptyList());
ls.searchByRating(rating);
}
#Test
public void searchForNegativeRatingYieldsNothing() {
int rating = -1;
LibrarySearch ls = new LibrarySearch(Arrays.asList(bookWithRating(rating)));
ArrayList<Book> results = ls.searchByRating(rating);
assertEquals(0, results.size());
}
you could do parameterized tests for this kind of things, but I think it does more harm than good for simple boundary tests (which should be about 4 tests).
I'm trying to run a program that will, if all goes well, be able to take a year and return the title of an album released in that year. I've given it 6 albums already and I'm now trying to actually print a title. I've fixed a few pretty frustrating errors, but this is one I've not seen before. The error appears at line 21, but I'm not sure what it means. Can anyone help?
package songselector;
import java.util.Scanner;
public class Main {
public class Album
{
int year; String title;
public Album () {
this.year = 0; this.title = null;
}
public Album (int year, String title) {
this.year = year; this.title = title;
}
}
class CAKE {
Album[] albums;
public CAKE () {
albums = new Album[6];
albums[0].year = 1994; albums[0].title = "Motorcade Of Generosity";
albums[1].year = 1996; albums[1].title = "Fashion Nugget";
albums[2].year = 1998; albums[2].title = "Prolonging The Magic";
albums[3].year = 2001; albums[3].title = "Comfort Eagle";
albums[4].year = 2004; albums[4].title = "Pressure Chief";
albums[5].year = 2011; albums[5].title = "Showroom of Compassion";
}
public void printAlbum (int y) {
System.out.println (albums[y].title);
}
}
public static void main(String[] args) {
new Main().new CAKE().printAlbum (0);
}
}
It means that you are trying to access / call a method on an object which is null. In your case, you initialized the array of Albums, but didn't initialize each of the albums in the array.
You need to initialize each album in the array:
albums = new Album[6];
albums[0] = new Album();
albums[0].year = 1994;
albums[0].title = "Motorcade Of Generosity";
...
Or even simpler (as #entonio pointed out):
albums = new Album[6];
albums[0] = new Album(1994, "Motorcade Of Generosity");
albums[1] = new Album(1996, "Fashion Nugget");
...
Since you have a proper constructor.
One more thing: don't call more than one method in each line, it will help you debugging.
When you allocate an array of objects, it's filled with null values. You need to create objects to fill them. Your albums[0] wasn't created, so trying to access its year field (even for writing) results in a NPE.