Since my post yesterday regarding constructing a link list from scratch I have made some progress.
I have encountered a new hurdle: storing an object within an object.
Lets say I have a 'book' class with the following attributes (forgetting all the set & get methods which I am familiar with):
private String title;
private int rating;
How would I then reference another class such as 'author' as obviously a book must have an author and many books may have the same or more then one author.
Here is my 'author' class attributes (again ignoring gets & sets):
String authorName;
String authorEmail;
Am I right in thinking I need to initiate an object of 'author' within the 'book' class as such:
private String title;
private int rating; //mine
private Author author = new Author();
Would I then have to set the attributes authorName and authorEmail everytime I made a new instance of 'book'?
Many thanks in advance for constructive feedback.
You don't necessarily need to instantiate the Author object right there where you declare the attribute. I would suggest something like passing in an already-instantiated Author either to the constructor of the Book class or to a setter. Then you could pass the same Author in to each Book you create that should be associated with it.
EDIT: added some code snippets:
For instance, if you made your Book constructor something like this:
public Book(String title, int rating, Author author) {
// set this.title, this.rating, and this.author to the passed-in parameters...
}
Then you would invoke it in code like this:
Author bob = new Author();
// You can set the name and email of the Author here using setters,
// or add them as args in the Author constructor
Book firstBook = new Book("The First Book", 1, bob);
Book secondBook = new Book("The Second Book", 2, bob);
This is a many to many relationship. You need your Author class to simply be a link between a person and a book. Then things will work out.
You might want some kind of singleton author list in order to prevent many copies of the same author. Either that, or you will definitely need to override the equals method of author.
If you use a singleton, you could have a getAuthor routine in your AuthorList object that either makes an author if it doesn't exist or fetches the already created author.
Your on the right track. You can use an ArrayList to be able to dynamically add new authors without having to resize anything. Let me clarify it for you:
class Book {
private String title;
private int rating;
private List<Author>authors = new ArrayList<Author>();
public Book(String title, int rating, Author author) {
this.title = title;
this.rating = rating;
authors.add(author);
}
public Book(String title, int rating, Author author) {
this.title = title;
this.rating = rating;
this.author = author;
}
public void addAuthor(Author a) {
authors.add(a);
}
public int numberOfAuthors() {return authors.size();}
}
class Author {
private String name;
private String email;
public Author(String name, String email) {
//...Same thing
}
}
class Main {
public static void main(String[] args) {
Book book = new Book("Java Programming", 5, new Author("Me", "me#gmail.com"));
Author contributingAuthor = new Author("My Friend", "cupcakes#gmail.com");
book.addAuthor(contributingAuthor);
}
}
Related
I create class Book with few values.
private int id;
private String title;
private String autorName;
private String autorSurname;
private int date;
private List<String> typeBook = Arrays.asList("autobiography", "classic", "comedy", "cookbook",
"crime story", "drama", "encyclopedia", "fantasty", "novel");
create a class constructor
public Book(int id, String t, String aName, String aSurname, int d, List<String> tBook)
{
this.id = id;
this.title = t;
this.autorName = aName;
this.autorSurname = aSurname;
this.date = d;
this.typeBook = tBook;
}
I want in Main create Object Book
Book b = new Book(1, "test", "xxx", "yyy", 2018, tBook);
And I have a question. How to get to the list,then choose the type of book ??
It seems that you are using the wrong structure for typeBook. A book can't be both an encyclopedia and a novel. It's just one type, selected from a set of choices. What you need is an enum:
enum BookType { ENCYCLOPEDIA, NOVEL, AUTOBIOGRAPHY, etc... };
class Book {
...
private BookType bookType;
public Book(int id, String t, String aName, String aSurname, int d, BookType bookType) {
...
}
}
Following DaveyDaveDave's and Jack Flump's helpful comments, you might want to put a book into a number of categories. For that, you can store an EnumSet in the Book class:
class Book {
...
private EnumSet<BookType> bookTypes;
...
}
And to make it easy to use, you can make the constructor take a variable argument list:
public Book(int id, String t, String aName, String aSurname, int d, BookType... bookTypes) {
...
this.bookTypes = EnumSet.of(bookTypes);
}
And call it like this:
Book tlotr = new Book(
12345,
"The Lord of the Rings",
"J.R.R.", "Tolkien",
1954,
BookType.CLASSIC, BookType.FANTASY, BookType.NOVEL);
If you want to avoid typing BookType. many times, you can add a static import statement at the top of your file:
import static my_package.BookType.*;
Then you can refer to the enum constants just by name. This only works if your source is in a named package. Also, if the enum is inside a class (such as book) then import static mypackage.Book.BookType.*;
I would look at DodgyCodeException's answer setting up a BookType enum. I'm also going to make the assumption that you're trying to get a List of Books by Type, since it's not entirely clear what you mean by the question.
If you're trying to get a List of Books, you're going to want to implement a MultiValueMap. Just for the sake of example:
Map<BookType, List<Book>> books = new HashMap<>();
books.computeIfAbsent(BookType.ENCYCLOPEDIA, () -> new ArrayList<Book>()).add(new Book(...));
Once you populate this datastructure. You can get all the books of a certain type.
List<Book> encyclopedias = books.get(BookType.ENCYCLOPEDIA);
Create getter for typeBook: List<String> getTypeBook() {return typeBook;}. Then use it: ...book.getTypeBook().get(index);
private List<String> typeBook = Arrays.asList("autobiography", "classic", "comedy", "cookbook", "crime story", "drama", "encyclopedia", "fantasty", "novel");
public void setTypeBook(List<String> typeBook){
this.typeBook = typeBook;
}
public List<String> getTypeBook(){
return this.typeBook;
}
public Book(int id, String t, String aName, String aSurname, int d, List<String> tBook){
this.id = id;
this.title = t;
this.autorName = aName;
this.autorSurname = aSurname;
this.date = d;
this.typeBook.addAll(tBook);
}
I have two classes in java: Movie and Book (it's a simplified example):
Book can have author:
public class Book {
public String author;
public Book(String a) {
this.author = a;
}
public String getAuthor(){
return author;
}
}
And Movie can have title:
public class Movie {
public String title;
public Movie(String t) {
this.title = t;
}
public String getAuthor(){
return title;
}
}
I'm trying to put all objects in a list like this:
ArrayList myList = new ArrayList();
Book book = new Book("William");
Movie movie = new Movie("Titanic");
myList.add(book);
myList.add(movie);
And afterwards I want to count how many books written by John do I have (or any other specific titles). However I can't apply getAuthor() or getTitle() method since java doesn't know what type of object it is
int counter = 0;
for (int i =0;i<myList.size();i++){
if (myList.get(i).getAuthor().equals("John") ){
counter +=1;
}
I would be able to use if clause, check every time for an object type, and apply different methods for different objects, but this is not viable, since in real-life case I have 20+ classes and it would make code very long and maintainable.
Can someone suggest a solution for this? Thanks in advance!
create an interface
public interface HasAuthor {
String getAuthor();
}
implement this interface in both your classes and use this:
List<HasAuthor> list = new ArrayList<>();
list.add(new Book());
list.add(new Movie());
long count = list.stream().filter(smth -> "John".equals(smth.getAuthor())).count();
You cannot be using ArrayList myList = new ArrayList(); in 2017. The world has moved on from that archaic and error-prone style of programming. Generics were added to the Java programming language in 2004, and since then, any attempt to use a generic class without a generic type argument issues a warning. Which brings me to the next issue:
You cannot be ignoring warnings in 2017. Actually, there was never a good time to be ignoring warnings. Your IDE ought to be issuing warnings when you try to do ArrayList myList = new ArrayList(); heed them.
So, bottom line is, you should not be putting books and movies in the same collection. If you have a book class that has an author, and a movie class that has a director, (I will ignore your example of movies having a title and returning that as "author", because it is nonsensical,) then you can have either an interface or an abstract base class called, say, Item, with a String getAuthor() method, which is implemented (overridden) in both Book and Movie.
Then, your myList will be an ArrayList<Item>, and since Item has a getAuthor() method, you will be able to do myList.get( 0 ).getAuthor() and it will work without having to know whether it is a book or a movie.
First, myList.get(i).getAuthor() == "John" won't work since strings need to be compared via equals() (look up tutorials on why).
Second, you need to know the type of your objects and cast accordingly in order to call a method (you could do without the cast using reflection but please don't try that at home). Thus when iterating over your list you need to check:
for (Object o : myList ) {
if (o instanceof Book && ((Book)o).getAuthor().equals("John") ){
counter +=1;
}
}
However, if you want one list to contain all books and movies you'd better provide a common interface or superclass:
//Make it abstract to not allow instances of this class directly
abstract class PieceOfArt {
private String creator;
public String getCreator() {
return creator;
}
}
class Book extends PieceOfArt {
//Access the creator as the author
//note that I do this just for demonstration purposes, just using getCreator() would be perfectly fine
public String getAuthor() {
return getCreator();
}
}
class Movie extends PieceOfArt {
//Access the creator as the director
//note that I do this just for demonstration purposes, just using getCreator() would be perfectly fine
public String getDirector() {
return getCreator();
}
}
List<PieceOfArt> myList = ...;
for( PieceOfArt p : myList ) {
if( p.getCreator().equals("John") {
...
}
}
Putting objects of different, unrelated types, such as Book and Movie, which don't have a common superclass (besides java.lang.Object) is bad practice.
You could define a common abstract superclass for these types, and then create a List of that type. For example:
public abstract class Product {
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;
}
}
class Book extends Product {
}
class Movie extends Product {
}
Create a List<Product> and work with that:
List<Product> products = new ArrayList<>();
Book book = new Book();
book.setTitle("Cooking");
book.setAuthor("Bob the Cook");
products.add(book);
Movie movie = new Movie();
movie.setTitle("Romance at sea");
movie.setAuthor("John");
products.add(movie);
int count = 0;
for (Product product : products) {
if (product.getAuthor().equals("John")) {
count++;
}
}
NOTE: Do not make the getAuthor method actually return the title in case of a Movie, that would make your program really confusing.
So I have created a simple class in Java like this:
public class Book {
private String author;
private String title;
public Book (String author, String title) {
this.author = author;
this.title = title;
}
}
public void checkInfo
Is there a way to parse a string (property) in order to get Book properties like this, instead of doing bookA.title ?
Book bookA = new Book("George Orwell","Animal Farm")
String property = "title";
System.out.print(bookA.property);
Thanks in adance!
If you really want to access many properties as String, I suggest you using a Map<String, String> like this :
public class Book
{
private Map<String, String> properties = new HashMap();
public void setProperty(String name, String value)
{
properties.set(name,string);
}
public String getProperty(String name)
{
return properties.get(name);
}
}
Now you can use like this :
Book book = new Book();
book.setProperty("title","Animal Farm");
book.setProperty("author","George Orwell");
System.out.println("Book: " + book.getProperty("title") + " by " + book.getProperty("author"))
You've created your Book as an object.
So, treat it like an object and add getters and setters.
In this case, that would be a method, getTitle() and a separate method getAuthor().
For more information on getters and setters, see the responses to this previous StackOverflow post
You can use reflection:
Field f = bookA.getClass().getDeclaredField("title");
f.setAccessible(true);
String title = (String) f.get(bookA);
System.out.println(title);
First of all, your code won't work because title is private. Second, I have no idea why you set Book class as static. Last, this (Java) is object oriented programming, so treat it like an object.
When you create a class you also add Getters & Setters to access the information inside. The code would look like this:
Class:
public class Book {
private String author;
private String title;
public Book (String author, String title) {
this.author = author;
this.title = title;
}
}
public String getTitle(){
return this.title;
}
public String getAuthor(){
return this.author;
}
Accessing the data:
Book bookA = new Book("George Orwell","Animal Farm")
System.out.print("Book: " + bookA.getTitle() + " by " + bookA.getAuthor());
This would return :
Book: Animal Farm by George Orwell
If you see these few lines from your code:
private String author; // both are private variables
private String title;
Here author and title both are private String . So you can't access these properties outside of the class.
So, you'll need to add public getters and setters that can be used to access the properties.
you should change you Object class.. add getter and setter method..
here is example :
public class Book{
String myauthor;
String mytitle;
public Book (String author, String title){
myauthor=author;
mytitle=title;
}
public void setAuthor(String Autor){
myauthor=author;
}
public String getAuthor(){
return myauthor;
}
}
and create setter and getter for 'title' too..
if you want to get the title / author, just simply call
Book.getAuthor();
If you don't want to have getter/setter method for your class ;you can define access modifier as protected with static keyword.For example is:
under com.test package-There are two class.One is Book class and other is BookInSamePackage class.In Book class;if you define attribute title as protected static String title then in BookInSamePackage class ;You can access like that :'Book.title'.If you want to use this title attribute in class of another package;then this class need to extend Book class and can access like this way:Book.title in child class of another package.
Given a class like the this:
public class Book {
private String title;
private int pages;
private static ArrayList<Book> ArrayBooks = new ArrayList<Book>();
public Book(String titl, int page){
title=titl;
pages=page;
}
}
How can I keep a reference to every Book in the List, in a way that doesn't require any special calls - ie done automatically by the constructor.
Also, the List should have a max size of 20 books; if the user attempts to create the 21st book, he shouldn't be able to.
Can anyone figure out a way to implement it?
To access the current object, you could use the this reference. So your constructor could look like this:
public Book(String title, int pages) {
this.title = title; // avoid abbreviations like "titl"
this.pages = pages; // or "page" just to avoid same variable names
ArrayBooks.add(this); // add new Book instance into the list
}
To avoid more than 20 entries, you have to check the current amount of entries in the list:
public Book(String title, int pages) {
this.title = title;
this.pages = pages;
(if ArrayBooks.size < 20) {
ArrayBooks.add(this); // add new Book instance into the list
}
}
And I recommend to read the Java naming conventions. So ArrayBooks should be arrayBooks.
Edit
To prohibit more than 20 Book instances, you'll need a factory method that takes care about creating the instances for you:
public static Book create(final String title, final int pages) {
if (ArrayBooks.size() < 20) {
final Book instance = new Book(title, pages);
ArrayBooks.add(instance);
return instance;
}
return null;
}
private Book(String title, int pages) { // "hide" the constructor, so the user can't use this instead of the factory
this.title = title;
this.pages = pages;
}
You could also throw an exception in your constructor, but I personally don't like that. It is up to you, which way you take.
Add this to the List, and throw an exception if the user attempts to create the 21st:
public class Book {
private static final List<Book> books = Collections.synchronizedList(new ArrayList<Book>());
private String title;
private int pages;
public Book(String title, int pages) {
if (books.size() == 20)
throw new IllegalStateException("Maximum number of books reached");
this.title = title;
this.pages = pages;
books.add(this);
}
}
I've added a call to Collections.synchronizedList() to add simple thread safety to prevent the maximum from being exceeded due to race conditions.
Also note the change of the list type to List<Book> (from ArrayList<Book>) - see Liskov substitution principle
I recommend to use list because it's good in some parameter then array.
public Book (String titl, int page) {
title = titl;
pages = page;
Arraylist.add(this); // add new Book in the list
}
For not giving more then 20
public Book (String titl, int page) {
title = titl;
pages = page;
if (Arraylist.size() < 20) {
Arraylist.add(this); // add new Book into the list
}
}
Try to apply first your own logic.
Problem Description
I have an abstract Paper class that contains common properties of all papers and one or more child classes of paper that add additional information for that type of paper. I then have a HashMap<String, Paper> to store multiple papers.
My application allows the user to update a paper by providing a the pid and then supplying the attributes and values to update. The issue I am having is how do I update the properties on the sub classes when all I have is the super class.
What is the best way/practice to handle this situation?
Class Structure
public abstract class Paper {
String pid;
String title;
String author;
}
public class Publication extends Paper {
int pages;
}
public class PHDThesis extends Paper {
String supervisor;
}
My Current Attempt
This is what I currently have** and it works by using instance of; but I feel there should be a better way to do this.
import java.util.*;
public class App {
public static abstract class Paper {
private String title;
private String author;
public Paper(String title, String author) {
this.title = title;
this.author = author;
}
public void update(String title, String author) {
this.title = title;
this.author = author;
}
}
public static class Publication extends Paper {
private int pages;
public Publication(int pages, String title, String author) {
super(title, author);
this.pages = pages;
}
public void update(String title, String author, int pages) {
super.update(title, author);
this.pages = pages;
}
}
public static class PHDThesis extends Paper {
private String supervisor;
public PHDThesis(String supervisor, String title, String author) {
super(title, author);
this.supervisor = supervisor;
}
public void update(String title, String author, String supervisor) {
super.update(title, author);
this.supervisor = supervisor;
}
}
public static void main(String[] args) {
HashMap<String, Paper> papers = new HashMap<String, Paper>();
papers.put("P001", new PHDThesis("My Super", "My PHD Title", "My Author"));
papers.put("P002", new Publication(22, "My Pub Title", "My Author"));
Paper p = papers.get("P001");
if (p instanceof PHDThesis) {
((PHDThesis)p).update("New Title", "New author", "New Super");
} else if (p instanceof Publication) {
((Publication)p).update("New Title", "New author", 33);
}
}
}
** reduced test code, actual code is much more complex and better laid out.
You can create an object called UpdateBundle with getters for each attribute.
Then the Paper class will have a method update(UpdateBundle) which each child will implement differently.
All you have to do is call that method for each child and they will know how to handle it.
On a separate note, i don't see why the paper class is abstract. You seem to have no abstract methods in it.
public abstract class Paper {
String pid;
String title;
String author;
public void update(PaperUpdateBundle bundle)
{
pid = bundle.getPID();
title = budnle.getTitle();
author = bundle.getAuthor();
}
}
public class Publication extends Paper {
int pages;
public void update(PaperUpdateBundle bundle)
{
super.update(bundle);
pages = bundle.getPages();
}
}
public class PHDThesis {
String supervisor;
public void update(PaperUpdateBundle bundle)
{
super.update(bundle);
supervisor = bundle.getSupervisor();
}
}
public interface PaperUpdateBundle
{
String getPID();
String getTitle();
String getAuthor();
int getPages();
String getSupervisor();
}
Create a method
public void update( Map<String, Object> parameters );
to all Papers and pull the relevant properties from it in the Paper implementations.
In Publication it might look like:
public void update( Map<String, Object> parameters ) {
super.update( parameters );
this.pages = parameters.get( "pages" );
}
The problem with the accepted answer is that it requires you to update all of the properties manually. If the list of properties changes, you have to change the update() method or things will get out of sync. In my experience this happens frequently. And then you've got to spend a lot of time trying to track down the bug.
A different way (I won't call it a "better" way) is to use reflection or some third party library to copy the fields. There are some tradeoffs, though. The advantage is that your code requires a lot less work and will (probably) have fewer bugs. The downside is that your code will be slower, less flexible, and lack compile-time checks.
I have sometimes used Jackson's ObjectMapper.convertValue() to do this. You can find other ways to do it here: Copy all values from fields in one class to another through reflection.