Generic repository using map - java

I have a homework that specifies to add to an existent project a generic repository layer. The problem that i face is the following. My repository should encapsulate a map that stores the data. What I have until now is the following:
public interface IDObject<T> {
public Comparable<T> getID();
}
public class Person implements IDObject<String> {
private String cnp;
private String name;
private String age;
public Person(String cnp, String name, String age) {
this.cnp = cnp;
this.name = name;
this.age = age;
}
public void setName(String name) { this.name = name; }
public void setCNP(String cnp) { this.cnp = cnp; }
public void setAge(String age) { this.age = age; }
public String getName() { return name; }
public String getCNP() { return cnp; }
public String getAge() {return age; }
public String toString() { return cnp + "-" + name + "-" + age; }
#Override
public Comparable<String> getID() { return getCNP(); } //basically the unique identifier
}
public class Repository<T extends IDObject<?????????>>{
private IMap map;
public Repository() {
map = new Map<???????, T>();
}
...
}
So my problem appears in Repository class. I want to store persons so I will do something like Repository<Person> repo = new Repository<Person>(); But the problem is I don't know how to construct the map in the Repository constructor. In other words I don't know the type of the key. I want the Person's cnp to be the key(which is of Type String), but if I force the map to define the keys as Strings, my repository is no longer generic, because if I want to add some Animal objects that have the key as an integer , the map should be like map = new Map<Integer, T>();.
So the question is how can I still use a construction like Repository<Person> repo = new Repository<Person>(); using the fact that Objects that are stored in a repository implement IDObject and knowing that a repository encapsulates a map which stores the data? How should I get to know the key so I can complete the repository class?

You will have to introduce another generic type:
public class Repository<T, O extends IDObject<T>>
After that you can introduce StringIDRepository as
public class StringIDRepository<O extends IDObject<String>> extends Repository<String, O>
Alternative is to use Map<Object, O>, but that would require you to have in your Repository class getById method that takes Object as argument.
Hope this will help!

Related

Is it advisable to create an empty POJO class in Java?

Suppose I have a superclass A and it has fields
class A {
String name;
int age;
setName(String name);
getName();
setAge(int age);
getAge()
}
I have multiple classes that extend A and add more fields along with the getters and setters.
Now there is another class, say B, which requires name and age, which is already provided by class A.
So should I go ahead and create class B without any field and simply it extends class A, or should I directly use class A?
class B extends A {}
P.S - I am using generics, which gives me a warning when I directly use superclass A, but the functionality is working fine. Please suggest.
Mostly the design wont be proper and justified if u just create a Class that do not have its own state, but yes it make sense if
A is abstract class i.e. you want to restrict the users to create an instance of A hence mark it abstract , then by creating B you are creating an implementation of A.
below is the example for the same
abstract class A{
protected String name;
protected int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class B extends A{}
also to make code more dynamic at runtime if u want to just use the parent class fields into ur function then probably u can do this
abstract class A{ // you can altogether remove 'abstract' and not create a B class
protected String name;
protected int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class B extends A{}
class C extends A{
protected String location;
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
now see the below method
public static <T extends A> void printName(List<T> list) {
for (T t : list) {
System.out.println(t.getName());
}
}
this qualifies for List<B>, List<C>
There is really no reason, that I can think of, where you need to extend a class without changing anything. Maybe you feel that you will need it in the future. This violates the YAGNI principal.
Just use Class A. You can make changes when they are needed.
It really is pointless. If it gives you a warning, there is probably a good reason for it. Unless you have a niche use case and know what you are doing, the empty class serves no purpose other than adding useless files to your project

Collections.sort that accept multiple parameters

I am new to Java. I am learning about Java Collections and I have question about writing a program to sort by attributes. So I have a class Course with these variables:
public class Course{
private String courseName;
private String courseDescription;
}
And another class Student that contains class Course which is a linked list of Course variables:
public class Student{
private String name;
private LinkedList<Course> courses;
}
public Student(String name) {
this.name = name;
this.courses = new LinkedList<Course>();
}
I want to write a method public void sortCourse() { } in Student class that should accept parameters to specify whether the sorting should be ascending or descending and also based on which course attribute to sort the courses, and print the sorted list of course. How can I write this method?
As Fureeish said in a comment: Make it accept a Comparator.
Like this:
public void sortCourse(Comparator<Course> comparator) {
this.courses.sort(comparator);
}
The caller would then write something like this:
// Sort ascending by name
student.sortCourse(Comparator.comparing(Course::getCourseName));
// Sort decending by name
student.sortCourse(Comparator.comparing(Course::getCourseName).reversed());
// Sort ascending by course level, then description
student.sortCourse(Comparator.comparing(Course::getCourseLevel)
.thenComparing(Course::getCourseDescription));
You can use Comparable interface to sort the courses. For that your Course class need to implements the Comparable interface.
public class Course implements Comparable <Course>{
private String courseName;
private String courseDescription;
public Course(String courseName, String courseDescription){
this.courseName = courseName;
this.courseDescription = courseDescription;
}
public int compareTo(Course c) {
return this.courseName.compareTo(c.courseName);
}
}
Now you can call Collections.sort(student.courses) method to sort the course list.
public class Student{
private String name;
private LinkedList<Course> courses;
public Student(String name) {
this.name = name;
this.courses = new LinkedList<Course>();
}
public void sortCourse(String sortOrder){
if(sortOrder.equals("asc")){
Collections.sort(this.courses);
} else {
Collections.sort(this.courses);
Collections.reverse(this.courses);
}
}
}

Object to string delimited format

I have set of objects of different types.
Ex : Employee emp, adress adr
These two classes have list of properties
public class Employee{
private Stringname;
private int age;
}
public class Adress {
private String HouseNo;
private string Street;
private string pin;
}
Each attribute is assigned with some 2 character value
Name (NA), age (AG), HouseNo(HN),Street(ST), pin(PN)
I need to construct a string with these data and delimit with a %
Output:
NA%Vidhya%AG%30%HN%80%ST%1st cross%PN%100100
Each class knows it own data best so I would let each class be responsible for generating the string. As I understand it the two char codes for each field are unique for each class and member and only used when generating the string so only the class would need them.
interface AttributeDescription {
String generateDescription();
}
public class Employee implements AttributeDescription {
//members...
public String generateDescription() {
return String.format(“NA%%%s%%AG%%%d”, name, age)
}
Then simply call this method for all objects implementing the interface.
AttributeDescription object = ...
String attr = object.generateDescription();
I don't think it can be generalized more than this given the requirements.
Update
It might be better to have a builder class for building the string to get a more unified behavior between classes. Here is an example
public class AttributeBuilder {
private builder = new StringBuilder();
public String getAttribute() {
return builder.toString();
}
public void add(String code, String value) {
if (value == null) {
return;
}
builder.append(code);
builder.append(‘%’);
builder.append(value);
builder.append(‘%’);
}
}
And then you would also have to implement add(...) methods for other data types in a similar fashion. The builder could then be used like
public String generateDescription() {
AttributeBuilder builder = new AttributeBuilder();
builder.add(“NA”, name);
builder.add(“AG”, age);
return builder.getAttribute();
}

Why Java Method Reference of instance method cannot be assigned to Consumer interface

Here is my Code :
public class SearchByLambda {
private Map<String,Consumer<Person>> searchCritertiaHolder = new HashMap<String,Consumer<Person>>();
private static final String AGED = "aged";
public SearchByLambda(){
searchCritertiaHolder.put(AGED, (Person p)-> {p.filterAgedPerson(p);} );
}
private Consumer<Person> getFilter(String personType){
return searchCritertiaHolder.get(personType);
}
public static void main(String[] args) {
SearchByLambda searchUsage = new SearchByLambda();
Person p = new Person(59,"shailesh");
Person p1 = new Person(58,"ganesh");
searchUsage.getFilter(AGED).accept(p);
searchUsage.getFilter(AGED).accept(p1);
Person.printAgedPersons();
}
}
class Person{
private static List<Person> agedPersons = new ArrayList<>();
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age,String name){
this.age = age;
this.name = name;
}
public void filterAgedPerson(Person person){
if(person.getAge() > 58){
agedPersons.add(person);
}
}
public static void printAgedPersons(){
for(Person person : agedPersons){
System.out.println(person.getName());
}
}
}
When I replace following Lambda expression
searchCritertiaHolder.put(AGED, (Person p)-> {p.filterAgedPerson(p);});
with
searchCritertiaHolder.put(AGED, Person::filterAgedPerson);
it gives me compilation error. I am using java 8 and and compiling through eclipse. Why is this so? Why cannot I assign method reference for instance method of any arbitrary object to consumer functional interface?
Your definition of filterAgedPerson takes a Person as an argument, even though it is not a static method. It doesn't need to, and it shouldn't if you want to use it as a Consumer<Person>. What you are ending up with is something compatible with BiConsumer<Person, Person>.
It might help to think of it this way: method references to non-static methods always take an "extra" argument which is used as this.
The easiest way for you to fix this with your current code structure is to modify the filterAgedPerson method to not take a Person as an argument
public void filterAgedPerson() {
if (this.getAge() > 58) {
agedPersons.add(person);
}
}
As an aside, you might want to also consider making your filters Predicate<Person> instead of Consumer<Person> and moving the results handling elsewhere. This will give you more flexibility as things get more complicated.

jackson serialization of nested objects

I have problem with jackson serialization of object by its interface.
I have class
class Point implements PointView {
private String id;
private String name;
public Point() {
}
public Point(String id, String name) {
this.id = id;
this.name = name;
}
#Override
public String getId() {
return id;
}
public String getName() {
return name;
}
}
which implements
interface PointView {
String getId();
}
and have class
class Map implements MapView {
private String id;
private String name;
private Point point;
public Map() {
}
public Map(String id, String name, Point point) {
this.id = id;
this.name = name;
this.point = point;
}
#Override
public String getId() {
return id;
}
public String getName() {
return name;
}
#JsonSerialize(as = PointView.class)
public Point getPoint() {
return point;
}
}
which implements
interface MapView {
String getId();
Point getPoint();
}
And have class
class Container {
private Map map;
public Container() {
}
public Container(Map map) {
this.map = map;
}
#JsonSerialize(as = MapView.class)
public Map getMap() {
return map;
}
}
I want serialize Container with Jackson and get result
{"map":{"id":"mapId","point":{"id":"pointId"}}}
But in fact I get result
{"map":{"id":"mapId","point":{"id":"pointId","name":"pointName"}}}
that have property "name" in nested object "point" although I specified serializition type of Point in Map (#JsonSerialize(as = PointView.class)). Interface PointView dont have method getName, but in result exists field "name" of Point.
If I remove annotation (#JsonSerialize(as = MapView.class)) from method getMap in class Container I get result
{"map":{"id":"mapId","name":"mapName","point":{"id":"pointId"}}}
Now point dont have property "name", but map have.
How can I get result
{"map":{"id":"mapId","point":{"id":"pointId"}}}
?
To get the desired result also the same method in interface must be annotated by #JsonSerialize
interface MapView {
String getId();
#JsonSerialize(as = PointView.class)
Point getPoint();
}
You can annotate the method like this:
#JsonIgnore
public String getName() {
return name;
}
Or if you want specific serialization in this use case, but normal serialization in others, you can use a #JsonView (see doc).
The reason it's serializing out the name is that the instance has the accessor getName(), even though interface does not.
Yes, you can use
#JsonSerialize(as=MyInterface.class)
public class ConcreteClass implements MyInterface { .... }
either on implementation class (as above), or on property that has value.

Categories