Java Streams usage with generics - java

I have a the below class:
#Data
public class PagedSchoolDto {
private final Integer count;
private final Map<String, List<School>> content;
private final String pagingState;
private final Boolean hasNext;
public PagedSchoolDto(final Slice<School> slice) {
this.content = slice.getContent().stream().collect(
Collectors.groupingBy(School::getId, () -> new TreeMap<>(new UUIDComparator()), Collectors.toList()));
this.count = slice.getContent().size();
this.hasNext = slice.hasNext();
this.pagingState = getPagingState(slice);
}
#Nullable
private static String getPagingState(final Slice<School> slice) {
if (slice.hasNext()) {
CassandraPageRequest pageRequest = (CassandraPageRequest) slice.nextPageable();
return pageRequest.getPagingState().toString();
} else {
return null;
}
}
}
Now, I want to make my code generic so that I can use this class for other object types as well like below:
#Data
public class PagedDto<T> {
private final Integer count;
private final Map<String, List<T>> content;
private final String pagingState;
private final Boolean hasNext;
public PagedDto(final Slice<T> slice) {
this.content = slice.getContent().stream().collect(
Collectors.groupingBy(<T>::getId, () -> new TreeMap<>(new UUIDComparator()), Collectors.toList()));
this.count = slice.getContent().size();
this.hasNext = slice.hasNext();
this.pagingState = getPagingState(slice);
}
#Nullable
private static <T> String getPagingState(final Slice<T> slice) {
if (slice.hasNext()) {
CassandraPageRequest pageRequest = (CassandraPageRequest) slice.nextPageable();
return pageRequest.getPagingState().toString();
} else {
return null;
}
}
}
All my classes have a property called ID which is type UUID so the comparator should work fine all generics types. The issue is I'm not sure how to write the Collectors.groupingBy's function and supplier code as the second snippet is giving compilation error.

Your generic type T is unspecified, e.g. 'Object' which does not provide the method getId. In order to access getId in the generic code, T must extend an interface with the getId method, which is implemented by any class that is used with PagedDto:
public interface WithId {
UUID getId();
}
public class School implements WithId {
private UUID id;
#Override
public UUID getId() {
return id;
}
public class PagedDto<T extends WithId> {
...

Related

Serialize/Deserialize a java.io.Serializable object but missing type id property

I try to serialize and deserialize an object DataSourceObject that wraps a Serializable object, but I don't know the type of the wrapped object.
When I deserialize the JSON, I get an exception:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.accor.assets.TestSerialization$DataSourceObject` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"#class":"com.accor.assets.TestSerialization$DataSourceObject","concreteObject":{"#class":"com.accor.assets.TestSerialization$HotelsListBean","version":"1.0","metaResponse":{"#class":"com.accor.assets.TestSerialization$MetaResponse","returncode":"0","date":"10/28/21 09:39:14 AM"},"hotelsList":{"#class":"com.accor.assets.TestSerialization$HotelsList","hotel":["java.util.ArrayList",[{"#class":"com.accor.assets.TestSerialization$Hotel","name":"My Hotel","code":"H001","nbstars":"4","countryCode":"G"[truncated 8 chars]; line: 1, column: 65]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1209)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362)
Here is a complete example to reproduce the problem:
public class TestSerialization {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = objectMapper();
HotelsListBean hotelsListBean = new HotelsListBean();
hotelsListBean.setVersion("1.0");
MetaResponse metaResponse = new MetaResponse();
metaResponse.setReturncode("0");
metaResponse.setDate("10/28/21 09:39:14 AM");
hotelsListBean.setMetaResponse(metaResponse);
HotelsList hotelsList = new HotelsList();
Hotel hotel = new Hotel();
hotel.setCode("H001");
hotel.setName("My Hotel");
hotel.setCountryCode("GB");
hotel.setNbstars("4");
hotelsList.getHotel().add(hotel);
hotelsListBean.setHotelsList(hotelsList);
DataSourceObject<HotelsListBean> dataSourceObject = new DataSourceObject<>(hotelsListBean);
String json = objectMapper.writeValueAsString(dataSourceObject);
System.out.println(json);
Object result = objectMapper.readValue(json, Object.class);
System.out.println(result);
}
private static ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(JsonGenerator.Feature.IGNORE_UNKNOWN);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
// Register support of other new Java 8 datatypes outside of date/time: most notably Optional, OptionalLong, OptionalDouble
objectMapper.registerModule(new Jdk8Module());
// Register support for Java 8 date/time types (specified in JSR-310 specification)
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private final T concreteObject;
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
public static class HotelsListBean implements Serializable {
private final static long serialVersionUID = 1L;
protected String version;
protected MetaResponse metaResponse;
protected HotelsList hotelsList;
public String getVersion() {
return version;
}
public void setVersion(String value) {
this.version = value;
}
public MetaResponse getMetaResponse() {
return metaResponse;
}
public void setMetaResponse(MetaResponse value) {
this.metaResponse = value;
}
public HotelsList getHotelsList() {
return hotelsList;
}
public void setHotelsList(HotelsList hotelsList) {
this.hotelsList = hotelsList;
}
}
public static class MetaResponse implements Serializable {
private final static long serialVersionUID = 1L;
protected String returncode;
protected String date;
public String getReturncode() {
return returncode;
}
public void setReturncode(String value) {
this.returncode = value;
}
public String getDate() {
return date;
}
public void setDate(String value) {
this.date = value;
}
}
public static class HotelsList implements Serializable {
private final static long serialVersionUID = 1L;
protected List<Hotel> hotel;
public List<Hotel> getHotel() {
if (hotel == null) {
hotel = new ArrayList<Hotel>();
}
return this.hotel;
}
}
public static class Hotel implements Serializable {
private final static long serialVersionUID = 1L;
protected String name;
protected String code;
protected String nbstars;
protected String countryCode;
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getCode() {
return code;
}
public void setCode(String value) {
this.code = value;
}
public String getNbstars() {
return nbstars;
}
public void setNbstars(String value) {
this.nbstars = value;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String value) {
this.countryCode = value;
}
}
}
I would like to know what is possible to successfully deserialize.
(I tried to add #JsonCreator on the DataSourceObject constructor but I get the same exception)
You need to have a no-args constructor for DataSourceObject. You have two options.
Make concreteObject not final:
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private T concreteObject;
private DataSourceObject() { }
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
Keep concreteObject final, but it must be assigned to null in the no-args constructor:
public static class DataSourceObject<T extends Serializable> implements Serializable {
private static final long serialVersionUID = -6026669040755678830L;
private final T concreteObject;
private DataSourceObject() {
concreteObject = null;
}
public DataSourceObject(final T concreteObject) {
this.concreteObject = concreteObject;
}
public T getConcreteObject() {
return concreteObject;
}
}
Either option will work.

Enum variable inside Enum class

Please help with best approach for following case:
There is table, which has about 20 columns.
Each column has its own short name, full name and type(number or String).
Each column type could have its own operator - For example, String - contains, equals; number - more, less, ==, !=
Each operator could have its own description.
I have to have object of Table class and be able to view all its columns, view short name and full name of each column, use operators based on column type.
I am trying to use enum, but I have no idea, how to connect specific column to specific type.
For example, how to connect "Id" column to "StringType" and "Services" column to "NumberType".
Could you please help.
class Table{
public enum Column {
Id("id", "ID number"),
Services("serv", "Services");
private final String shortName;
private final String fullName;
Column(String shortName, String fullName) {
this.shortName = shortName;
this.fullName = fullName;
}
public String getShortName() {
return shortName;
}
public String getFullName() {
return fullName;
}
}
public enum StringType{
contains("String contain another string"),
equals("String equal a string");
private final String placeholder;
StringType(String fullName) {
this.placeholder = fullName;
}
public String getPlaceholder() {
return placeholder;
}
}
public enum NumberType{
more("value that is more than input"),
less("value that is less than input");
private final String placeholder;
NumberType(String fullName) {
this.placeholder = fullName;
}
public String getPlaceholder() {
return placeholder;
}
}
}
Like any other class, enum types can implement interfaces. You can use this to your advantage:
public interface DataType {
// Deliberately empty. This is a marker interface.
}
public enum StringType
implements DataType {
// ...
}
public enum NumberType
implements DataType {
// ...
}
public enum Column {
Id("id", "ID number", StringType.class),
Services("serv", "Services", NumberType.class);
private final String shortName;
private final String fullName;
private final Class<? extends DataType> type;
Column(String shortName, String fullName, Class<? extends DataType> type) {
this.shortName = shortName;
this.fullName = fullName;
this.type = type;
}
// ...
}
If you plan to actually use these to compare data, you can add methods to the DataType interface:
public interface DataType<T> {
Class<T> getDataClass();
BiPredicate<? super T, ? super T> getTest();
default boolean test(T value1, T value2) {
return getTest().test(value1, value2);
}
default boolean testObjects(Object value1, Object value2) {
Class<T> type = getDataClass();
return test(type.cast(value1), type.cast(value2));
}
}
public enum StringType
implements DataType<String> {
contains("String contain another string", String::contains),
equals("String equal a string", Object::equals);
private final String placeholder;
private final BiPredicate<? super String, ? super String> test;
StringType(String fullName,
BiPredicate<? super String, ? super String> test) {
this.placeholder = fullName;
this.test = test;
}
public String getPlaceholder() {
return placeholder;
}
#Override
public BiPredicate<? super String, ? super String> getTest() {
return test;
}
#Override
public Class<String> getDataClass() {
return String.class;
}
}
public enum NumberType
implements DataType<Number> {
more("value that is more than input",
(n1, n2) -> n1.doubleValue() > n2.doubleValue()),
less("value that is less than input",
(n1, n2) -> n1.doubleValue() < n2.doubleValue());
private final String placeholder;
private final BiPredicate<? super Number, ? super Number> test;
NumberType(String fullName,
BiPredicate<? super Number, ? super Number> test) {
this.placeholder = fullName;
this.test = test;
}
public String getPlaceholder() {
return placeholder;
}
#Override
public BiPredicate<? super Number, ? super Number> getTest() {
return test;
}
#Override
public Class<Number> getDataClass() {
return Number.class;
}
}
public enum Column {
Id("id", "ID number", StringType.class),
Services("serv", "Services", NumberType.class);
private final String shortName;
private final String fullName;
private final Class<? extends DataType<?>> type;
Column(String shortName, String fullName, Class<? extends DataType<?>> type) {
this.shortName = shortName;
this.fullName = fullName;
this.type = type;
}
// ...
}
Java has a Class class that can hold types as its objects, it even works with the primitive types.
It doesn't have a constructor but a factory method called forName() that creates a class using the String provided as its parameter.(although this is considered bad practice).
The better way to obtain a reference to a type is using a class literal. A Class object representing its underlying class can also be obtained from any of its objects using the getClass() method.
Here are some ways through which you can create an object representing a class/type using the Class class:
Using Class Literals
Class<?> type1 = int.class;
Class<?> type2 = boolean.class;
Factory Method
Class<?> type1 = Class.forName("java.lang.String");
It's better to avoid this method since string parsing is involved which could lead to unnecessary runtime errors as pointed out by #VGR.
Using an object
String str = "";
Class type<?> = str.getClass();
You can add an additional variable of type Class<?>and do something like this:
public enum Column {
Id("id", "ID number", String.class),
Services("serv", "Services", Number.class);
Contact("cont", "Contacts", long.class);
private final String shortName;
private final String fullName;
private final Class<?> type;
Column(String shortName, String fullName, Class<?> type) {
this.shortName = shortName;
this.fullName = fullName;
this.type = type;
}
public String getShortName() {
return shortName;
}
public String getFullName() {
return fullName;
}
public Class<?> getType() {
return type;
}
}
Class is a powerful class, it has various methods like getName() and getMethods(). More on it here.
Note:
Class is commonly used for reflection which has major drawbacks, it can basically break encapsulation and also involves some performance overhead.
But if you merely use this additional field for the purpose of storing type information, it shouldn't be a big deal.
** This is in assumption that you want to map your column types to Java types!**

How do I use passed in generic Lists of objects and return a generic object with my Java method?

I am trying to make my application more efficient by using Java generics, but I don't have a lot of generics experience. I am trying to do this:
My Two Model Objects:
public class Person {
private String firstName;
private String lastName;
private int age;
// ...
// Getters and Setters Here
}
public class Car {
private String make;
private String model;
private int year;
// ...
// Getters and Setters Here
}
My Result Objects:
private class PersonResult {
private boolean success;
private String notes;
private int score;
// Getters and Setters Here
}
private class CarResult {
private boolean success;
private String notes;
private int score;
private double price;
// Getters and Setters Here
}
My Main Class:
public class MyClass{
public mainMethod(){
List<Person> listOfPersons = new ArrayList<>();
List<Car> listOfCars = new ArrayList<>();
listOfPersons.add(// a Person object);
// ...
listOfCars.add(// a Car object);
// ...
PersonResult = resultMethod(listOfPersons);
CarResult = resultMethod(listOfCars);
}
private <E,T> E resultMethod(List<T> listOfData)
{
Engine engine = EngineFactory.getEngine();
// if(listOfData.contains(Person.class))
if (listOfData instanceof List<Person>)
{
PersonResult personResult = new PersonResult();
perosnResult = engine.getPersonResult(listOfData);
return personResult;
}
// else if(listOfData.contains(Car.class))
else if (listOfData instanceof List<Car>)
{
CarResult carResult = new CarResult();
carResult engine.getCarResult(listOfData);
return carResult;
}
return null;
}
}
However my IDE is complaining about:
1 Illegal generic type for instanceof
2 (for the return value) Incompatible types: Required: E - Found PersonResult
As you can see, I also tried listOfData.contains(Person.class), that took care of error #1, but not the return error.
You cannot test for generic parameters by instanceof. instanceof will be evaluated at runtime. But the compiler removes the generic types. They are not present at runtime. For example List<Integer> will be the same as List at runtime.
You can use a simple implementation without generics here:
private PersonResult resultMethodPerson(List<Person> listOfData) {
return EngineFactory.getEngine().getPersonResult(listOfData);
}
private CarResult resultMethodCar(List<Car> listOfData) {
return EngineFactory.getEngine().getCarResult(listOfData);
}

Store Java enum's String value instead of enum value itself in DynamoDB

I'm using DynamoDB and I would like to store the enum's String values instead of the enum itself.
For instance, I have this enum:
public enum Source {
BREACH("breach"),
LEAKAGE("leakage");
private final String desc;
Source(String desc) { this.desc = desc; }
public String desc() { return desc; }
}
...and this "entity":
#DynamoDBTable(tableName = "Alerts")
public final class Alert implements Serializable {
private static final long serialVersionUID = 4012517315640518044L;
#DynamoDBHashKey(attributeName = "AlertId") // Partition Key or Hash Attribute
private String alertId;
#DynamoDBTypeConvertedEnum
#DynamoDBAttribute(attributeName = "Source")
private Source type;
// Constructor(s), Getter(s), Setter(s), ToString, etc...
}
With the #DynamoDBTypeConvertedEnum annotation, the value that gets saved is BREACH, but I want breach.
{
"AlertId": { "S": "a083168d-cb23-4ec8-ab80-a1c16955c4b8" },
"Source": { "S": "BREACH" },
...
"CreatedAt": { "S": "2017-05-03T14:07:36.395Z" }
}
Any clues? I did try "converters" (not fully, I couldn't make it work though) but I think I have to end up doing one for each enum type since they are all different.
You can code the Alert class like this i.e. define the attribute as String and design the getter and setter to send/receive enum object (i.e. Source).
Alert class:-
#DynamoDBTable(tableName = "Alerts")
public final class Alert implements Serializable {
private static final long serialVersionUID = 4012517315640518044L;
private String alertId;
private String type;
#DynamoDBHashKey(attributeName = "AlertId")
public String getAlertId() {
return alertId;
}
#DynamoDBAttribute(attributeName = "Source")
public Source getType() {
if (type != null)
return Source.valueOf(type);
else
return null;
}
public void setAlertId(String alertId) {
this.alertId = alertId;
}
public void setType(Source type) {
this.type = type.desc();
}
}
Create Alert:-
Stores the value as expected on database table. The get item from DynamoDB table also works fine.
public Boolean createAlert(String alertId, Source source) {
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
Alert alert = new Alert();
alert.setAlertId(alertId);
alert.setType(source);
dynamoDBMapper.save(alert);
return true;
}
Override toString() this should work.
public enum Source {
BREACH("breach"),
LEAKAGE("leakage");
private final String desc;
Source(String desc) { this.desc = desc; }
public String desc() { return desc; }
#Override
public String toString() { return desc; }
}

Refactoring Java class with multiple responsibilities

public class MyObject
{
public static enum Type {A, B, C, D;}
public static final int ID_MAIN = 1;
public static final int ID_MAIN_UK = 2;
public static final int ID_MAIN_US = 3;
public static final int ID_SUB = 4;
// lots more constants here
public static final String DESCRIPTION_1 = "Desc Full Name";
public static final String DESCRIPTION_2 = "Desc2 Full Name";
// lots more constants here
private int id;
public MyObject(final int id)
{
this.id = id;
}
//simple getter
public int getID() { return this.id;}
// real responsibility of the class is in the following two methods
public static String getDescription()
{
switch(id)
{
case MyObject.ID_MAIN:
case MyObject.ID_MAIN_UK:
return MyObject.DESCRIPTION_1;
case MyObject.ID_SUB:
return MyObject_Description_2;
default:
// throw IllegalArgException
}
}
public static Type getType(int id)
{
switch(id)
{
case MyObject.ID_MAIN:
case MyObject.ID_SUB:
return Type.A;
case MyObject.ID_MAIN_UK:
case MyObject.ID_MAIN_US:
return Type.B;
default:
return Type.Undefined;
}
}
}
Basically, there is an ID that maps to both a description and a type. This ID is passed in during construction of the class and it should map to a set of constants already contained in the class. If the id is not part of the list of constants, an error is thrown when trying to get the description that maps to the id and an 'Unknown' type is return if the type is queried. The ID maps a description to a set of constants. The same ID maps to a certain Type (defined as an enum).
This code is pretty ugly because there are tons of constants defined at the top, which makes the switch statements pretty bloated. Is there a simple way to refactor this without changing the public interface? It seems trivially simple, but it seems pretty ugly no matter how you slice it. How can I simplify these mappings to make the code more concise?
I was thinking about representing the mappings in a text file and having a manager class that held simple containers in a hashmap. When the manager class is constructed, it would create the objects by reading the text file and map them to an ID. When the manager is queried with the ID, it would just call the corresponding get method, for instance:
class Manager
{
private HashMap<int, MyObject> objectMap;
public Manager() {} //construct the object map
public String getDescription(int id) { return objectMap.get(id).getDescription();}
public Type getType(int id) { return objectMap.get(id).getType();}
}
class DataContainer
{
private String description;
private Type type;
public DataContainer(String desc, Type type) {//set mem vars}
public String getDescription() //simple getter
public Type getType() //simple getter
}
But this solution seems too complicated. Is there a better solution, preferably one that would keep everything in one class?
You can do something like following. This would be much cleaner and manageable.
public enum Type
{
MAIN(1, "Main Description"),
MAIN_UK(2, "Main UK Description"),
//....
//Define all the types
//....
UNKNOWN(-1, "Unknown Type");
private int id;
private String description;
private Type(int id, String description)
{
this.id = id;
this.description = description;
}
public static Type getById(int id)
{
for (Type type : Type.values())
{
if (id == type.getId())
{
return type;
}
}
return Type.UNKNOWN;
}
public final int getId()
{
return id;
}
public final String getDescription()
{
return description;
}
}
public class MyObject
{
private int id;
private Type type;
public MyObject(int id)
{
this.id = id;
this.type = Type.getById(id);
}
public int getId()
{
return id;
}
public Type getType()
{
return type;
}
public String getDescription()
{
return type.getDescription();
}
}
In Java enums can have methods. For example following one accepts ID and description and provides some accessors.
public enum Type {
MAIN(1, "desc1"),
UK(2, "desc2"),
SUB(4, "desc4");
private int id;
private String desc;
Type(int id, String desc) {
this.id = id;
this.desc = desc;
}
public String getDescription() {
return desc;
}
public int getType() {
//return id;
return 1+2 + 3+ id;
}
}
You could use that to improve design.

Categories

Resources