I have a PersonFactory interface as follows:
#FunctionalInterface
public interface PersonFactory<P extends Person> {
P create(String firstname, String lastname);
// Return a person with no args
default P create() {
// Is there a way I could make this work?
}
}
The Person class:
public class Person {
public String firstname;
public String lastname;
public Person() {}
public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
}
I want to be able to instantiate my Persons like this:
PersonFactory<Person> personFactory = Person::new;
Person p = personFactory.create(); // does not work
Person p = personFactory.create("firstname", "lastname"); // works
Is there a way I could make the Java compiler automatically choose the right constructor by matching the signature of PersonFactory.create() ?
One way would be to have the following:
default P create() {
return create(null, null);
}
But I'm not sure that's what you wanted. The problem is that you can't make a method reference refer to 2 different methods (or constructors). In this case, you want Person::new to refer to the constructor taking no parameters and the constructor taking 2 parameters, which is not possible.
When you have:
#FunctionalInterface
public interface PersonFactory<P extends Person> {
P create(String firstname, String lastname);
}
and use it like
PersonFactory<Person> personFactory = Person::new;
Person p = personFactory.create("firstname", "lastname");
you have to realize that the method-reference Person::new refers to the constructor taking 2 parameters. The next line just invokes it by passing the parameters.
You could also write it more explicitely using a lambda expression:
PersonFactory<Person> personFactory = (s1, s2) -> new Person(s1, s2); // see, we have the 2 Strings here
Person p = personFactory.create("firstname", "lastname");
Related
Recently I came into a situation where the builder pattern was very strong, but I had the need to subclass. I looked up some solutions and some suggested generics while others suggested normal subclassing. However, none of the examples I looked at had required fields in order to even begin building an object. I wrote a tiny example to illustrate where I'm getting stuck. At every turn I kept running into a wall of problems where things would return the wrong class, can't override static methods, returning super() returns the wrong data type, etc. I have a feeling there is no way out except excessive use of generics.
What is the correct way to go in this situation?
Tester
import person.Person;
import person.Student;
public class Tester
{
public static void main(String[] args)
{
Person p = Person.builder("Jake", 18).interest("Soccer").build();
// Student s = Student.builder(name, age) <-- It's weird that we still have access to pointless static method
// Student s = Student.builder("Johnny", 24, "Harvard", 3).address("199 Harvard Lane") <-- returns Person builder, not student
Student s = ((Student.Builder)Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build(); // really bad
}
}
Person Class
package person;
import java.util.ArrayList;
import java.util.List;
public class Person
{
// Required
protected String name;
protected int age;
// Optional
protected List<String> interests = new ArrayList<>();
protected String address = "";
protected Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
public List<String> getInterests() { return interests; }
public String getAddress() { return address; }
// person.person does not allow builder construction
// unless all required fields are provided
/* Problem: I have to repeat the constructor fields here, very annoying */
public static Builder builder(String name, int age)
{
Person p = new Person(name, age);
return new Builder(p);
}
public static class Builder
{
Person reference;
protected Builder(Person reference)
{
this.reference = reference;
}
public Builder address(String address)
{
reference.address = address;
return this;
}
public Builder interest(String interest)
{
reference.interests.add(interest);
return this;
}
public Person build()
{
return reference;
}
}
}
Student Class
package person;
import java.util.ArrayList;
import java.util.List;
public class Student extends Person
{
// Required
protected String school;
protected int year;
// Optional
protected List<String> subjects = new ArrayList<>();
// This looks good
public Student(final String name, final int age, final String school, final int year)
{
super(name, age);
this.school = school;
this.year = year;
}
public String getSchool() { return school; }
public int getYear() { return year; }
public List<String> getSubjects() { return subjects; }
/* Here's where my issues are:
* Override doesn't compile on static methods but how else can I describe that I want to
* override this functionality from the Person class?
*
* Extending 'Person' does not enforce that I need to provide 'name', 'age', etc like it would
* if this was a normal design pattern using the 'new' keyword. I have to manually drag fields
* from 'person' and place them here. This would get VERY messy with an additional class
*
* User can STILL call the Person builder on a Student object, which makes no sense. */
/*#Override*/ public static Builder builder(String name, int age, String school, int year)
{
Student s = new Student(name, age, school, year);
return new Builder(s);
}
public static class Builder extends Person.Builder
{
// Student reference; <--- this should not be needed since we already
// have a variable for this purpose from 'Person.Builder'
public Builder(final Student reference)
{
super(reference);
}
/* Things begins to get very messy here */
public Builder subject(String subject)
{
((Student)reference).subjects.add(subject);
// I guess I could replace the reference with a student one, but
// I feel like that infringes on calling super() builder since we do the work twice.
return this;
}
#Override public Student build()
{
// I can either cast here or
// rewrite 'return reference' every time.
// Seems to infringe a bit on subclassing.
return (Student)super.build();
}
}
}
What you write here :
Student s = ((Student.Builder)Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build(); // really bad
is indeed not very natural and you should not need to cast.
We expect rather something like :
Student s = Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build();
Besides all casts you did in the implementation of Student.Builder are also noise and statements that may fail at runtime :
/* Things begins to get very messy here */
public Builder subject(String subject) {
((Student)reference).subjects.add(subject);
return this;
}
#Override public Student build() {
return (Student)super.build();
}
Your main issue is the coupling between the Builder classes and the building methods.
A important thing to consider is that at compile time, the method binding (method selected by the compiler) is performed according to the declared type of the target of the invocation and the declared type of its arguments.
The instantiated type is considered only at runtime as the dynamic binding is applied: invoking the method bounded at compile time on the runtime object.
So this overriding defined in Student.Builder is not enough :
#Override public Student build() {
return (Student)super.build();
}
As you invoke :
Student.builder("Jack", 19, "NYU", 1).address("Dormitory").build();
At compile time, address("Dormitory") returns a variable typed as Person.Builder as the method is defined in Person.Builder :
public Builder address(String address){
reference.address = address;
return this;
}
and it not overriden in Student.Builder.
And at compile time, invoking build() on a variable declared as Person.Builder returns a object with as declared type a Person as the method is declared in Person.Builder as :
public Person build(){
return reference;
}
Of course at runtime, the returned object will be a Student as
Student.builder("Jack", 19, "NYU", 1) creates under the hood a Student and not a Person.
To avoid cast to Student.builder both from the implementation and the client side, favor composition over inheritancy :
public static class Builder {
Person.Builder personBuilder;
private Student reference;
public Builder(final Student reference) {
this.reference = reference;
personBuilder = new Person.Builder(reference);
}
public Builder subject(String subject) {
reference.subjects.add(subject);
return this;
}
// delegation to Person.Builder but return Student.Builder
public Builder interest(String interest) {
personBuilder.interest(interest);
return this;
}
// delegation to Person.Builder but return Student.Builder
public Builder address(String address) {
personBuilder.address(address);
return this;
}
public Student build() {
return (Student) personBuilder.build();
}
}
You can now write :
Student s = Student.builder("Jack", 19, "NYU", 1)
.address("Dormitory")
.build();
or even that :
Student s2 = Student.builder("Jack", 19, "NYU", 1)
.interest("Dance")
.address("Dormitory")
.build();
Composition introduces generally more code as inheritancy but it makes the code
both more robust and adaptable.
As a side note, your actual issue is enough close to another question I answered 1 month ago.
The question and its answers may interest you.
A few thoughts as background
Static methods are not so great,
they make unit testing more difficult.
It is fine to put the builder as a static, nested class, but if you are using a builder to construct a class you should make the constructor not-public.
I prefer to have the builder be a separate class in the same package and to make the constructor (of the class that is created by the builder) package access.
Limit the builder constructor parameters.
I'm not a fan of using a class hierarchy for builders.
The Person and Student classes each have a builder.
Some Code
public class PersonBuilder
{
private String address;
private int age;
private final List<String> interestList;
private String name;
public PersonBuilder()
{
interestList = new LinkedList<>();
}
public void addInterest(
final String newValue)
{
// StringUtils is an apache utility.
if (StringUtils.isNotBlank(newValue))
{
interestList.add(newValue);
}
return this;
}
public Person build()
{
// perform validation here.
// check for required values: age and name.
// send all parameters in the constructor. it's not public, so that is fine.
return new Person(address, age, interestList, name);
}
public PersonBuilder setAddress(
final String newValue)
{
address = newValue;
return this;
}
public PersonBuilder setAge(
final int newValue)
{
age = newValue;
return this;
}
public PersonBuilder setInterestList(
final List<String> newValue)
{
interestList.clear();
if (CollectionUtils.isNotEmpty(newValue))
{
interestList.addAll(newValue);
}
return this;
}
public PersonBuilder setName(
final String newValue)
{
name = newValue;
return this;
}
}
public class Person
{
private Person()
{
}
Person(
final String addressValue,
final int ageValue,
final List<String> interestListValue,
final String name)
{
// set stuff.
// handle null for optional parameters.
}
// create gets or the fields, but do not create sets. Only the builder can set values in the class.
}
I am just trying to add some objects to an ArrayList in eclipse, but i keept getting an error (Syntax error, insert "... VariableDeclaratorId" to complete FormalParameterList) under the 'persons.add(one);'. Any idea what I am doing wrong?
package thequestion;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class PersonComparator implements Comparator<Person>{
#Override
public int compare(Person o1, Person o2) {
return 0;
}
Person one = new Person("Kevin", "Gresmer");
List<Person> persons = new ArrayList<Person>();
persons.add(one);
public void sortByLastName(List people) {
Comparator comp = new PersonComparator();
Collections.sort(people, comp);
}
}
public class Person {
private String firstName = null;
private String lastName = null;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
The line persons.add(one) is a statement, and you can't have a statement outside a block of code (method, constructor, etc.). Also, your Comparator should contain code related to the comparison you are doing. I don't think it's the right place to store a list or implement the sortByLastName() method.
You can only initialize the instance variables in class and outside the methods.
You initialized persons. Then to apply methods, you need to put that line into some methods. So, may be you can update your Sort Method or create new one and make reference to that method from your sort method.
The first option as explained in the above comment.
class PersonComparator implements Comparator{
#Override
public int compare(Person o1, Person o2) {
return 0;
}
Person one = new Person("Kevin", "Gresmer");
List<Person> persons = new ArrayList<Person>();
private void addPersons(){
persons.add(one);
}
//persons.add(one);
public void sortByLastName(List people) {
addPersons();
Comparator comp = new PersonComparator();
Collections.sort(people, comp);
}
}
The point is we cannot write anything except the Instance variables. We have to cover them with methods. I hope this will solve your issue.
put below under sortByLastName method. Currently below code snippet not lying under any method.
persons.add(one);
Correct code should be
public void sortByLastName(List people) {
persons.add(one);
Comparator comp = new PersonComparator();
Collections.sort(persons , comp);
}
please note :- But my answer is in terms of compilation error. Ideally comparator should have code only related to comparison. Nothing else.
Obviously, you cannot do persons.add(...) in the middle of the class declaration.
You should certainly add one and persons in a main in the class Person, and keep your Comparator class clean with only the logic of comparison. The main will allow you to test your code.
The same idea for the sortByLastName(...) you can declare it as static in Person class.
Then you can test with the main, e.g.
// In class `Person`
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
Person one = new Person("Kevin", "Gresmer");
persons.add(one);
Person two = new Person("Elvis", "Presley");
persons.add(two);
System.out.println(sortByLastName(persons));
}
Note for the above to provide a sensible output, you need to add a toString() method to the Person class.
This is the test class that I have an error in and I cant figure out what it is exactly.
import java.util.*;
public class EmployeeTest
{
public static void main(String[] args) {
Employee e = new Employee();
e.setId("100012");
e.setLastname("Smith");
ResponsibilityDecorator d;
d = new Recruiter(e);
d = new CommunityLiaison(e);
d = new ProductionDesigner(e);
System.out.println(e.toString());
}
}
And this the class that links to the test class
public class Employee
{
String id;
String lastname;
Employee(String id, String lastname)
{
this.id=id;
this.lastname=lastname;
}
EmploymentDuties eduties=new EmploymentDuties();
public EmploymentDuties getDuties()
{
return eduties;
}
public String toString(){
return "Duties for this employee: "+eduties.jobtitles;
}
public void setId(String id)
{
this.id = id;
}
public void setLastname(String lastname)
{
this.lastname = lastname;
}
}
There is no no-args constructor in Employee. Add parameters to use the existing constructor in EmployeeTest
Employee e = new Employee("100012", "Smith");
the statements
e.setId("100012");
e.setLastname("Smith");
are then redundant and can be removed.
Your class Employee defines exactly one constructor: Employee(String, String). Make sure you call it from the EmployeeTest or define a no parameter constructor.
The default constructor is the constructor provided by the java in the absence of any constructor provided by the you.
Once you supplies any constructor , the default constructor is no longer supplied.
you created constructor
Employee(String id, String lastname)
{
and using like
Employee e = new Employee(); //not possible
The default constructor is the no-argument constructor automatically generated unless you define another constructor.
This is the default constructor :
Employee() {}
Then you can instantiate objects like :
Employee e = new Employee();
if you define at least one constructor explicitly , the default constructor is not generated.
Is that possible to initialize object directly as we can do with String class in java:
such as:
String str="something...";
I want to do same for my custom class:
class MyData{
public String name;
public int age;
}
is that possible like
MyClass obj1={"name",24};
or
MyClass obj1="name",24;
to initialize object?
or how it can be possible!
Normally, you would use a constructor, but you don't have to!
Here's the constructor version:
public class MyData {
private String name;
private int age;
public MyData(String name, int age) {
this.name = name;
this.age = age;
}
// getter/setter methods for your fields
}
which is used like this:
MyData myData = new MyData("foo", 10);
However, if your fields are protected or public, as in your example, you can do it without defining a constructor. This is the closest way in java to what you want:
// Adding special code for pedants showing the class without a constuctor
public class MyData {
public String name;
public int age;
}
// this is an "anonymous class"
MyData myData = new MyData() {
{
// this is an "initializer block", which executes on construction
name = "foo";
age = 10;
}
};
Voila!
If you have a class Person:
public class Person {
private String lastName;
private String firstName;
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
You can actually create a new Person object and initialize its firstName and lastName with the following:
Person person = new Person(){{
setFirstName("My FirstName");
setLastName("MyLastName");
}}
This is used quite often when defining Spring Configuration using Java code instead of XML configuration.
You have to make a constructor method for the object, which takes in parameters of the fields you want values for.
Example:
public myClass( int age, String name)
{
this.age = age;
this.name = name;
}
Then in the class you want this:
myClass class = new myClass(24, "name");
I know that with constructors, but any alternative way is present or not?
No, there are no alternatives to constructors.
That's basically one of the fundamental guarantees of the language. An object can't be constructed by any other means than through its constructors and there's no alternative syntax then the usual new ConstructorName(...).
The closest idea I can come up with would be to have a static factory method called say, mc:
class MyClass {
...
public static mc(String name, int age) {
return new MyClass(name, age);
}
}
and then do
import static some.pkg.MyClass.mc;
...
MyClass obj1 = mc("name",24);
It is possible with the keyword new and using constructors, but not like the String, that is a very special kind of object.
class MyData{
public MyData(String name, int age) {
this.name = name;
this.age = age;
}
public String name;
public int age;
}
Then you can instantiate your class this way:
MyData myData = new MyData("name", 24);
package com.company;
public class InitializationOfObject {
int a ;
int b ;
InitializationOfObject(){
}
InitializationOfObject( int r , int n){
this.a = r;
this.b = n;
System.out.println("Object initialization by constructor ");
}
void methodInitialization(int k, int m){
System.out.println("object initialization via method");
this.a = k;
this.b = m;
}
void display(){
System.out.println("k = " +a+ "m = "+b);
}
public static void main(String... arg){
InitializationOfObject io = new InitializationOfObject();
InitializationOfObject io2 = new InitializationOfObject(45,65);
io.a = io2.a;
io.b = io2.b;
io.display();
io.methodInitialization(34,56);
io.display();
io.a = 12;
io.b = 24;
System.out.println("object initialization via refrence");
System.out.println("a = "+io.a+" "+ " b ="+io.b);
}
}
//Object initializatian by construtor
k = 45m = 65
object initializaion via method
k = 34m = 56
object initialization via reference
a = 12 b =24
There are two types of Constructors in java.
Default constructor
Parameterized constructor
You should create a parameterized constructor to create your object.
The following does what you want, but not in the way that you would expect.
So in a class calling MyData, you would use
Public MyData x = new MyData();
#PostConstruct public void init() {
x.setName("Fering");
x.setAge(18);
}
So once the object is construsted, these commands are run, which allows you to populate the object before anything else runs.
So with this you do not have to use anonymous subclasses, or create new constructors, you can just take the class and then use its functions, before anything else would.
There is no alternative to constructors (along with new operator) in java during the object initialization. You have mentioned as
String str = "something"
you can initialize string that way, because String is a literal in java. Only literals can initialized that way. A a composite object can not initialized, but only can be instantiated with the new operator with the constructors.
Hi i have the following code:
public List<Person> findAll() {
List<Person> copy = new ArrayList<Person>();
for (Person person : personer) {
copy.add(person);
}
return copy;
}
But when i test this i only retrieve the following and not the value:
[Person#15c7850, Person#1ded0fd,
Person#16a9d42]
How do i get the values and not like above. Where i am inserting the person the code looks like this:
public boolean insert(String name, String nbr) {
if (containsName(name)) {
return false;
}
Person person = new Person(name, nbr);
personer.add(person);
return true;
}
and here is my Person class:
class Person {
private String name;
private String nbr;
public Person (String name, String nbr) {
this.name = name;
this.nbr = nbr;
}
public String getName() {
return name;
}
public String getNumber() {
return nbr;
}
}
You're already receiving the objects you want.
What you see is an internal representation of these objects.
You must iterate through them and call their respective methods to see the information you probably want to see.
If you're not satisfied with these results, you must override toString to provide you with more meaningful information.
Update:
after seeing your edit, you should add toString similar to this one in your Person class:
#Override
public String toString() {
return "Name: " + name + ", number: " + nbr;
}
By the way, you're storing nbr as a string, and it's obvious it should be an integer. So, I'd suggest changing its type to an int or Integer.
You are getting a List object back. You can use the Person object to get the data that you need. To get to the Person objects, iterate over the list.
List<Person> people = findAll();
for Person p : people {
String phoneNumber = p.phoneNumber();
String name = p.Name();
}
Override the toString() method in the Person class if you want a better description when printing the results.
Put something like this in the class Person (don't change the method name!):
public String toString() {
return name;//change this line
}
You are printing out an Object that has the default toString inherited from the Object class. This will print out the type of object it is and its location in memory (ie: Person#1ded0fd).
If you'd like it to see something else, you can override the toString method within your class:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public String toString() {
return this.name;
}
}
If your class looked like the above, this would allow you to do something like this:
Person p = new Person("John");
System.out.println(p);
> John
You can also just grab it as is and print out any information you want from it without overriding the toString method.
Person p = new Person("John");
System.out.println(p.getName());
> John
What value or class Person's property you aspect to retrieve from the ArrayList? This kind of value(Person#15c7850, etc) shows that the Person's object random id that assigned by JVM when you use
System.out.print(copy).