I am trying to write some generic code and facing issue. Here is code
public abstract class AbstractService<D extends IDTO> {
public String ex(D dto) {
return null;
}
}
public class AService extends AbstractService<TestA> {
#Override
public String ex(TestA dto) {
return "a";
}
}
public class BService extends AbstractService<TestB> {
#Override
public String ex(TestB dto) {
return "b";
}
}
class TestA impements IDTO {
}
class TestB impements IDTO {
}
So as you can see, its really simple code, one AbstractService with bounded param that extends IDTO.
Two implementation of service AService and BService which uses their respective DTO.
Not there is another class that need to call ex() method on basis of runtime instance.
here I am facing the problem.
public class TestAll {
public void executeRequest(final IDTO dto){
// serviceMap contains list of all Services here A and B
serviceMap.get(type).ex(dto);
}
}
Problem on line build().
The method build(capture#5-of ? extends IDTO) in the type AbstractService is not applicable for the arguments (IDTO)
Could someone help to fix this issue?
I found the reason why it was giving me the error. It was my mistake as I was trying to build a map with the help of Spring and was using bounded approach.
It was my previous code.
#Autowired
public void setServicesList(List<AbstractService<IDTO>> abstractServices) {
serviceMap = abstractServices.stream().collect(Collectors.toMap(AbstractService::getType, Function.identity()));
}
and I had to remove the bounded approach and now it's working.
public void setServicesList(List<AbstractService> abstractServices) {
serviceMap = abstractServices.stream().collect(Collectors.toMap(AbstractService::getType, Function.identity()));
}
In case you know what type of service holds the Map, you could do following:
// define your service map
private final Map<String, AbstractService<? extends IDTO>> serviceMap = Map.of(
"a", new AService(),
"b", new BService());
// cast `AbstractServise` from the map into required type:
public void executeRequest(final TestA dto){
((AbstractService<TestA>)serviceMap.get("a")).ex(dto);
}
public void executeRequest(final TestB dto){
((AbstractService<TestB>)serviceMap.get("b")).ex(dto);
}
Related
I have following class/interface structure (can't modify the source):
public interface Car {}
public class CarA implements Car{
public String getASpecifics() {...}
}
public class CarB implements Car{
public String getBSpecifics() {...}
}
public class Summary {...}
I want to have a generic way of creating Summray for concrete implementations of Car interface which will be open to adding new implementations. My approach is following:
public CarSummarizer {
public interface SummaryGenerator<T extends Car> {
Summary generateSummary(T car);
}
static {
SummaryGenerator<CarA> aGen = c -> {... c.getASpecifics(); ...}
SummaryGenerator<CarB> bGen = c -> {... c.getBSpecifics(); ...}
}
}
Now I'd like to store aGen and bgen in a Map. I want to parametrize it so that I can offer one only public static method which accepts Car car and based on it's class object (car.getClass()) uses correct SummaryGenerator implemenation. That should look something like following:
public static Summary getSummaryForCar(Car car) {
return map.get(car.getClass()).generateSummary(car);
}
I don't know how to declare and instantiate that Map so that it's fully type-safe (i.e. doesn't allow inserting pair (CarC.class, SummaryGenerator<CarD>)). I'd like something like this:
public static <T extends Car> Map<Class<T>, SummaryGenerator<T>> map = new LinkedHashMap<>();
static {
// after instantiation
map.put(CarA.class, aGen);
map.put(CarB.class, BGen);
}
// also support following
public static <T extends Car> void addSummaryGenerator(T car, SummaryGenerator<T> sg) {
map.put(car.getClass(), sg);
}
That doesn't work because generics can't be declared on variables like they can be on functions.
I guess I could define new class public class SummarizerStorage<T extends Car> and the place map inside and just delegate calls. That seems like an overkill and ugly. I feel like it should be done somehow directly.
Declaring map like Map<Class<? extends Car>, SummaryGenerator<? extends Car>> would allow paring of Class<> and SummaryGenerator<> of sibling types. I want to allow only same type pairs.
You can do this with compile-time safety if you wrap your map and only allow to put entries of which the value SummaryGenerator<T> matches a key of Class<T>. To get the SummaryGenerator you need to cast but since you ensured that you only added entries that actually can be cast to SummaryGenerator<T>, this is safe to do. If you try to add an incompatible SummaryGenerator for a given implementation of Car, it would result in a compile error.
public class SummaryGeneratorStorage {
private static final Map<Class<? extends Car>, CarSummarizer.SummaryGenerator<? extends Car>> map = new HashMap<>();
// provides type safety to only add a SummaryGenerator<T> for a key of Class<T>
public static <T extends Car> void add(Class<T> clazz, CarSummarizer.SummaryGenerator<T> sg) {
map.put(clazz, sg);
}
#SuppressWarnings("unchecked")
public static <T extends Car> CarSummarizer.SummaryGenerator<T> get(T car) {
// this cast is safe since the add-method only
// allows a SummaryGenerator<T> to be added for a key of Class<T>
return (CarSummarizer.SummaryGenerator<T>) map.get(car.getClass());
}
private SummaryGeneratorStorage() { }
}
In order to get a Summary, you retrieve the registered SummaryGenerator for the Car and call the implemented generateSummary method. If no SummaryGenerator is found for the passed implementation of Car, just throw an exception or handle it a different way. Note that the signature of add takes a Class and not a Car object since we don't need an instance at this point to make the reference to an implementation of Car.
public class CarSummarizer {
public interface SummaryGenerator<T extends Car> {
Summary generateSummary(T car);
}
static {
// here you cannot pass incompatible implementations of Car (compile-time safety)
SummaryGenerator<CarA> aGen = c -> new Summary(c.getASpecifics());
SummaryGeneratorStorage.add(CarA.class, aGen); // with variable
SummaryGeneratorStorage.add(CarB.class, c -> new Summary(c.getBSpecifics())); // direct
}
public static <T extends Car> Summary getSummary(T car) {
SummaryGenerator<T> generator = SummaryGeneratorStorage.get(car);
if (generator != null) {
return generator.generateSummary(car);
} else {
throw new IllegalArgumentException("no summary generator found");
}
}
}
Here is a test class for the above code. A simple Summary containing a name-field was used and the implementations of Car return A, B or C in their getXSpecifics() methods:
public class CarSummarizerTest {
#Test
public void testCarA() {
CarA car = new CarA();
Summary summary = CarSummarizer.getSummary(car);
Assert.assertEquals(car.getASpecifics(), summary.getName());
}
#Test
public void testCarB() {
CarB car = new CarB();
Summary summary = CarSummarizer.getSummary(car);
Assert.assertEquals(car.getBSpecifics(), summary.getName());
}
#Test
public void testCarC() {
CarC car = new CarC();
Assert.assertThrows(IllegalArgumentException.class, () -> {
CarSummarizer.getSummary(car);
});
}
}
For completeness the other classes:
public class CarA implements Car {
public String getASpecifics() { return "A"; }
}
public class CarB implements Car {
public String getBSpecifics() { return "B"; }
}
public class CarC implements Car {
public String getCSpecifics() { return "C"; }
}
public class Summary {
private final String name;
public Summary(String name) { this.name = name; }
public String getName() { return name; }
}
I have a class which calls two singleton classes FirstClass and SecondClass as below. Is there a way to access the data computed in FirstClass in the SecondClass. In this case I don't want to make external service call in second class since the first class has already called it. Instead, just use the data (stored in first class function) in the second data function. What are the ways to do it.
public class CallingFunction() {
List<String> generateData() {
return Lists.newArrayList(new FirstClass(), new SecondClass())
}
#Singleton
public class FirstClass() extends interface {
public String function() {
//operations. This function calls multiple services and stores ouput to hashMap
Map<String, String> hashedData = Maps.newHashMap();
hashedData.put(dataFromAnotherService);
return hashedData.get("abc");
}
}
#Singleton
public class SecondClass() extends interface {
public String function() {
//Use hashedData here instead of invoking the service again.
//Other operations
return "data";
}
}
Yes you can achieve the functionality by something below logic:
#Singleton
public class FirstClass() extends interface {
private static FirstClass instance;
private Map<String, String> hashedData = new HashMap<>();
public String function() {
//operations. This function calls multiple services and stores ouput to hashMap
hashedData.put(dataFromAnotherService);
return hashedData.get("abc");
}
public Map<String, String> getHashedData() {
return this.hashedData;
}
public static FirstClass getInstance() {
if (instance == null) instance = new FirstClass();
return instance;
}
}
#Singleton
public class SecondClass() extends interface {
public String function() {
FirstClass instance = FirstClass.getInstance();
// instance.getHashedData() here instead of invoking the service again.
//Other operations
return "data";
}
}
First, the code that runs correctly:
The content in DynamoDB table is :
"test_field" : "x:x:x"
Entity POJO has:
#DynamoDBTypeConverted(converter = SingleFieldConverter.class)
#DynamoDBAttribute(attributeName = "test_field")
private SingleField singleField;
with appropriate getter and setter methods.
Converter class is:
public class SingleFieldConverter implements DynamoDBTypeConverter<String, SingleField> {
#Override
public String convert(SingleField singleField) {
// removed for clarity
}
#Override
public SingleField unconvert(String s) {
// removed for clarity
}
and it works!
But when I change the DynamoDB item to have:
"test_field" : [
"x:x:x",
"x:x:x"
]
and my POJO object to:
#DynamoDBTypeConverted(converter = SingleFieldConverter.class)
#DynamoDBAttribute(attributeName = "test_field")
private List<SingleField> singleField;
and converter class to be like:
public class SingleFieldConverter implements DynamoDBTypeConverter<String, List<SingleField>> {
#Override
public String convert(List<SingleField> singleField) {
// removed for clarity
}
#Override
public List<SingleField> unconvert(String s) {
// removed for clarity
}
}
it throws:
DynamoDBMappingException
. . . . . [test_field]; could not unconvert attribute
. . .Caused by: com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: expected S in value {L: [{S: x:x:x:,}, {S: x:x:x:,}],}
Similar examples I found on the web allegedly work, but my example does not. What am I missing, please help?
Based on the error it looks like the converter return a String. To avoid confusion, I suggest to rename the SingleFieldConverter to SingleFieldListConverter.
I confirm that complex type are supported. I use this Generic without any problems:
public abstract class MapToJsonStringConverter<K, V> implements DynamoDBTypeConverter<String, Map<K, V>> {
private final static ObjectMapper mapper= new ObjectMapper();
#Override
public String convert(Map<K, V> map) {
try {
return mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
Each unconvert are usage specific.
I did found come similar looking posts on generic issues, but non of them captured my problem in a way I could understand it. I hope someone can help me with this case.
I tried lots of things like removing the "? extends" from different places and playing with "? super" to see if another error would point me in a direction of a solutions.
The following code:
final ResponseBase response = executor.execute(myRequest);
Gives me the following compiler error:
"The method execute(capture#6-of ? extends RequestBase) in the type CommandExecutor is not applicable for the arguments (RequestBase)"
The complete code listing:
public class MainClass {
private final static Map<Class<? extends RequestBase>, CommandExecutor<? extends RequestBase, ? extends ResponseBase>> MAP = new HashMap<>();
public static void main(String[] args) {
final DummyCommandExecutor dummyCommandExecutor = new DummyCommandExecutor();
MAP.put(MyRequest.class, dummyCommandExecutor);
final RequestBase myRequest = new MyRequest();
myRequest.setRequestString("this is my request");
final CommandExecutor<? extends RequestBase, ? extends ResponseBase> executor = MAP.get(myRequest.getClass());
final ResponseBase response = executor.execute(myRequest);
System.out.println(response.getResponseString());
}
}
interface CommandExecutor<T, R> {
R execute(T object);
}
class DummyCommandExecutor implements CommandExecutor<MyRequest, MyResponse> {
#Override
public MyResponse execute(MyRequest request) {
final MyResponse response = new MyResponse();
response.setResponseString(request.getRequestString());
return response;
}
}
class MyResponse extends ResponseBase {
}
class ResponseBase {
String responseString;
public String getResponseString() {
return this.responseString;
}
public void setResponseString(String responseString) {
this.responseString = responseString;
}
}
class MyRequest extends RequestBase {
}
class RequestBase {
String requestString;
public String getRequestString() {
return this.requestString;
}
public void setRequestString(String requestString) {
this.requestString = requestString;
}
}
You can't do this without casting. While you know that the map will return the correct command executor for a given type, the compiler does not, so you need to tell the compiler to not care:
final CommandExecutor<RequestBase,ResponseBase> executor = (CommandExecutor) MAP.get(myRequest.getClass());
you cannot just match ? with T
an option is just to do :
final CommandExecutor executor = MAP.get(myRequest.getClass());
in which option of course you can get run time exception if the map returns smth not castable
I have a class for example
public class Example<T> {...}
I would like to instantiate class Example with a specific type class which I know. Pseudocode would look something like that
public Example<T> createTypedExample(Class exampleClass, Class typeClass) {
exampleClass.newInstance(typeClass); // made-up
}
So that this would give me same result
Example<String> ex = new Example<String>();
ex = createTypedExample(Example.class, String.class);
Is it possible in Java?
Since, the return type i.e. the class of the new instance is fixed; there's no need to pass it to the method. Instead, add a static factory method to your Example class as
public class Example<T> {
private T data;
static <T> Example<T> newTypedExample(Class<T> type) {
return new Example<T>();
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Now, here's how you would create generic Example instances.
// String
Example<String> strTypedExample = Example.newTypedExample(String.class);
strTypedExample.setData("String Data");
System.out.println(strTypedExample.getData()); // String Data
// Integer
Example<Integer> intTypedExample = Example.newTypedExample(Integer.class);
intTypedExample.setData(123);
System.out.println(intTypedExample.getData()); // 123