Function with variable parameter type - java

I have these two functions which are basically the same:
private Order getOrder( List<Order> orders, String gcsId )
{
Predicate<Order> predicate = c -> c.getGcsId().equals( gcsId );
Order obj = orders.stream().filter( predicate ).findFirst().get();
return obj;
}
private OrderLine getOrderLine( List<OrderLine> orderLines, String gcsId )
{
Predicate<OrderLine> predicate = c -> c.getGcsId().equals( gcsId );
OrderLine obj = orderLines.stream().filter( predicate ).findFirst().get();
return obj;
}
The only difference is the type of the first parameter. How can I create a single function with the first parameter having a variable type?
Edit: This is how I call those functions in another method of the same class:
Order order = getOrder( orders, orderId );
OrderLine orderLine = getOrderLine( order.getOrderLines(), orderLineId );

First you should create an interface like that and your classes should implement this interface:
public interface IOrder {
String getGcsId();
}
public class Order implements IOrder {
// Content of your class
}
public class OrderLine implements IOrder {
// Content of your class
}
After that you can write your method like that using generics:
private <T extends IOrder> T getOrder( List<T> orders, String gcsId )
{
Predicate<T> predicate = c -> c.getGcsId().equals( gcsId );
T obj = orders.stream().filter( predicate ).findFirst().get();
return obj;
}

Either make them implement a common interface, which defined the getGcsId() method, and use
private <T extends CommonInterface> T getFirstWithGcsId(List<T> orders, String gcsId) {
Predicate<T> predicate = c -> c.getGcsId().equals(gcsId);
T obj = orders.stream().filter(predicate).findFirst().get();
return obj;
}
Or change the signature of the method to pass it a function which knows how to extract the gcsId:
private <T> T getFirstWithGcsId(List<T> orders, String gcsId, Function<T, String> gcsIdExtractor) {
Predicate<T> predicate = c -> gcsIdExtractor.apply(c).equals(gcsId);
T obj = orders.stream().filter(predicate).findFirst().get();
return obj;
}
But this method is so simple that, frankly, if you don't have any common interface, I would just inline it. It's basically one line of code:
Order firstOrder = orders.stream().filter(o -> o.getGcsId().equals(gcsId)).findFirst().get();
You should also avoid calling get() on an Optional in general. Rather prefer something like
Order firstOrder = orders.stream().filter(o -> o.getGcsId().equals(gcsId)).findFirst().orElseThrow(() -> new IllegalStateException("no first order with gcsId " + gcsId));

If you give a List of Order you return an Ornder, if you give an define a generic method instead:
private <T extends Foo> T getOrder(List<T > orders, String gcsId) {
Predicate<T> predicate = c -> c.getGcsId().equals(gcsId);
T obj = orders.stream().filter(predicate).findFirst().get();
return obj;
}
where Foo interface is
interface Foo {
String getGcsId();
}
class OrderLine implements Foo {
#Override
public String getGcsId() {
return something;
}
}
class Order implements Foo {
#Override
public String getGcsId() {
return something;
}
}

You should create an Interface. See below:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class SomeFunctionsRefactored {
public static void main(String[] args) {
Order simpleOrder = new SimpleOrder();
Order lineOrder = new LineOrder();
String gcsId;
List<Order> simpleOrders = new ArrayList(Arrays.asList(simpleOrder));
List<Order> lineOrders = new ArrayList(Arrays.asList(lineOrder));
Order order = getOrder(simpleOrders, "Hi I'm a simple order");
System.out.println(order.getGcsId()); // Hi I'm a simple order
order = getOrder(lineOrders, "Hi I'm a line order");
System.out.println(order.getGcsId()); // Hi I'm a line order
}
private static Order getOrder(List<Order> orders, String gcsId) {
Predicate<Order> predicate = c -> c.getGcsId().equals(gcsId);
return orders.stream().filter(predicate).findFirst().get();
}
The interface:
interface Order {
public String getGcsId();
}
class SimpleOrder implements Order {
String gcsId = "Hi I'm a simple order";
public String getGcsId() {
return gcsId;
}
}
class LineOrder implements Order {
String gcsId = "Hi I'm a line order";
public String getGcsId() {
return gcsId;
}
}

One way that you can perform instance of test after taking list as a parameter, but your List is parameterized.
if you have anything that is parameterized,
List<Foo> fooList = new ArrayList<Foo>();
The Generics information will be erased at runtime. Instead, this is what the JVM will see
`List fooList = new ArrayList();`
This is called type erasure.
The JVM has no parameterized type information of the List (in the example) during runtime.
Since the JVM has no information of the Parameterized type on runtime,
there's no way you can do an instanceof of ArrayList.
So better you keep your both function to work.

Related

Lambda representing a getter method from either a parent class or a child class

I have some code below representing a parent and child Pojo, and a simple validator that pulls two values off them representing ranges, to verify that start < end. I want to validator to be generic enough that it can accept two field getter methods at construction time, and then be able to be passed a POJO to perform the range check on. However, I have been unable to get this to type check properly. I have tried having the validator constructor taking all of the following:
Function<Pojo, Integer> //Fails on constructing vlad2 - "Incompatible types in lambda expression: Expected Pojo but found ExtendedPojo".
Function<? extends Pojo, Integer> //Fails on getRangeStart.apply(pojo) - "(capture<? extends Pojo>) in Function cannot be applied to Pojo"
Function<Object, Integer> //Fails on constructing both vlad and vlad2 - "Incompatible types in lambda expression: Expected Object but found ExtendedPojo"
Code:
import java.util.function.Function;
class Pojo {
private Integer rangeOneStart;
private Integer rangeOneEnd;
public Pojo(Integer rangeOneStart, Integer rangeOneEnd) {
this.rangeOneStart = rangeOneStart;
this.rangeOneEnd = rangeOneEnd;
}
public Integer getRangeOneStart() {
return rangeOneStart;
}
public Integer getRangeOneEnd() {
return rangeOneEnd;
}
}
class ExtendedPojo extends Pojo {
private Integer rangeTwoStart;
private Integer rangeTwoEnd;
public ExtendedPojo(Integer rangeOneStart, Integer rangeOneEnd, Integer rangeTwoStart, Integer rangeTwoEnd) {
super(rangeOneStart, rangeOneEnd);
this.rangeTwoStart = rangeTwoStart;
this.rangeTwoEnd = rangeTwoEnd;
}
public Integer getRangeTwoStart() {
return rangeTwoStart;
}
public Integer getRangeTwoEnd() {
return rangeTwoEnd;
}
}
interface SomeValidatorInterface<T> {
boolean isValid(T obj);
}
class MyValidator implements SomeValidatorInterface<Pojo> {
private Function<Pojo, Integer> getRangeStart;
private Function<Pojo, Integer> getRangeEnd;
MyValidator(Function<Pojo, Integer> getRangeStart, Function<Pojo, Integer> getRangeEnd) {
this.getRangeStart = getRangeStart;
this.getRangeEnd = getRangeEnd;
}
#Override
public boolean isValid(Pojo pojo) {
Integer start = getRangeStart.apply(pojo);
Integer end = getRangeEnd.apply(pojo);
return end > start;
}
}
class Main {
public static void main(String args[]) {
ExtendedPojo pojo = new ExtendedPojo(1,2,3,4);
MyValidator vlad = new MyValidator(Pojo::getRangeOneStart, Pojo::getRangeOneEnd);
System.out.println(vlad.isValid(pojo));
MyValidator vlad2 = new MyValidator(ExtendedPojo::getRangeTwoStart, ExtendedPojo::getRangeTwoEnd);
System.out.println(vlad2.isValid(pojo));
}
}
Since the validator is being used per instance just provide a specific instance method as Supplier<Integer>
MyValidator(Supplier<Integer> getRangeStart, Supplier<Integer> getRangeEnd) {
this.getRangeStart = getRangeStart;
this.getRangeEnd = getRangeEnd;
}
// ...
ExtendedPojo pojo = new ExtendedPojo(1,2,3,4);
MyValidator vlad = new MyValidator(pojo::getRangeOneStart, pojo::getRangeOneEnd);
If you don't want to use such specific construction you need to move the range getter to the common interface or at least the Pojo class and override this in ExtendedPojo

Java 8 - Map between class to one of its function

I have multiple types of objects, I'd like to generalise the 'id' of the objects in a way that will dynamically change what field is selected as the id.
Example
public class ObjectA{
//Attribute name attA
private String attA;
.... More attributes
public String getAttA(){
return attA
}
.....More getters/setters
}
public class ObjectB{
//Attribute named attB
private String attB;
.... More attributes
public String getAttB(){
return attB
}
.... More getters and setters
}
Id like to be able to run something like this:
Map<????, ????> customIdMap = new HashMap<>();
//We decide that ObjectA main attribute is AttA
customIdMap.add(ObjectA.class, ObjectA::getAttA);
//We decide that ObjectB main attribute is AttB
customIdMap.add(ObjectB.class, ObjectB::getAttB);
Then I'll be able to have a list of general objects and ill be able to retrieve their ids from the map if it is a known object with:
public String getCustomId(Object object){
if(customIdMap.contains(object.getClass()){
//Parameters are messed up, but this is the general idea of how
//i thought this would look
return customIdMap.get(object.getClass()).apply(object);
}
}
The code above does not run since getAttA is a call to a none static method in a static context so i assume this maybe should be wrapped in some kind of generic object.
Can it be done?
Preferably you change ObjectA and ObjectB to have a common interface. If that's not possible you can put them into a map like this:
Map<Class<? extends Object>, Function<Object, String>> map = new HashMap<>();
map.put(ObjectA.class, a -> ((ObjectA) a).getAttA());
map.put(ObjectB.class, b -> ((ObjectB) b).getAttB());
EDIT:
Or if you would like to encapsulate it into a typesafe heterogeneous container:
public static class ToIdMap {
private final Map<Class<?>, Function<Object, String>> map = new HashMap<>();
public <X> void put(Class<X> clazz, Function<X, String> func) {
map.put(clazz, (Function<Object, String>) func);
}
public String toIdString(Object o) {
return map.get(o.getClass()).apply(o);
}
}
EDIT2: Note that neither of these solutions work for subclasses, but it could be supported by traversing the class hierarchy in toIdString.
Your wording is a bit unclear, but I assume you want to get the ID of an object, even when they are different classes. This is the problem that interfaces solve.
You can create an interface, with one method called getId(), which will return the id. Then, you can just call getId() on any type of object with an id.
For example:
public interface Identifiable {
String getId();
}
public class ObjectA implements Identifiable {
// same for ObjectB
#Override
public String getId() {
return id;
}
}
Then, in your code:
Identifiable i1 = new ObjectA();
Identifiable i2 = new ObjectB();
System.out.println(i1.getId());
System.out.println(i2.getId());
EDIT:
It still looks like an interface is the cleanest way of solving your problem. For completeness, the following will work:
Map<Class, Function<?, String> map = new HashMap<>();
map.put(Object1.class, (Object1 o) -> o.getAttrA); // repeat for ObjectB
It can then be called with:
if (obj instanceof Object1) return map.get(Object1.class).apply((ObjectA) obj);
Ended up doing this weird solution:
class Mapping<T> {
private Function<T, String> idFunc;
public Mapping(Function<T, String> idFunc) {
this.idFunc = idFunc;
}
public String apply(T obj) {
return idFunc.apply(obj);
}
}
}
private Map<Class, Mapping> mappings = new HashMap<>();
mappings.put(ObjectA.class, new Mapping<>(ObjectA::getAttA);
mappings.put(ObjectB.class, new Mapping<>(ObjectB::getAttB);
public String getObjectID(Object object){
String id = null;
if(mappings.containsKey(object.getClass())){
id = mappings.get(object.getClass()).apply(object);
}
return id;
}

Java return nested type parameters

I have a problem with Java's Generic System.
In my program is a wrapper for lists, that should have a method to return it's inner list:
public class Wrapper<T, S extends List<T>> {
private S list;
public Wrapper(S list) {
this.list = list;
}
public S get() {
return list;
}
}
Then there is a Context that holds a Map with different Wrappers and a method that returns the list of the wrapper associated with the id:
public class Context {
private Map<String, Wrapper> map;
public Wrappers() {
map.put("a", new Wrapper(ArrayList<String>());
map.put("b", new Wrapper(LinkedList<Integer>());
}
public <T, S extends List<T>> S getList(String id) {
return map.get(id).get();
}
}
Now when I call getList() I want to have a compiler warning or at least a way to realise an error before a ClassCastException gets thrown.
public class Receiver {
public doSomething() {
Context c = new Context();
c.createWrappers();
// Ok
ArrayList<String> list1 = c.getList("a");
LinkedList<Integer> list2 = c.getList("b");
// Compiler error or some way do check in getList().
ArrayList<Integer> list3 = c.getList("a");
LinkedList<String> list4 = c.getList("b");
}
}
I've actually tried a lot of things like changing the Wrapper definition to:
public class Wrapper<T, S extends List>
But when I want to implement the get() function I run into a problem I can either define the function like this:
public List<T> get() {
return list;
}
or like this
public S get() {
return list;
}
In the first example it would still be possible to do this.
public doSomething() {
//...
LinkedList<String> list = c.getList("a");
}
and in the second example it would be possible to do this.
public doSomething() {
//...
ArrayList<Integer> list = c.getList("a");
}
Is there any way to define the get method in a way like this?
public S<T> get() {
return list;
}
It seems to me like there is no way to check both the type of the list and the type of the elements at the same time.
The compiler has no way of knowing what return type is associated with the particular string you passed (strings cannot be made type-safe).
However, you could replace strings with type-safe marker objects:
class ListId<T> {
public ListId(string name) { ... }
public static final ListId<ArrayList<String>> A = new ListId<>("a");
public static final ListId<LinkedList<Integer>> B = new ListId<>("b");
}
public T getList<T>(ListId<T> id)

Using Generics: Instantiating an object of a generic class with parent and child values in generic

The question from the exam states:
Write a class called Registration which can store variables of type T, where T can be replaced with String,
Person, Student, etc.. It should be possible to instantiate Registration as follows:
Registration<String> r1 = new Registration<String>();
Registration<Person> r2 = new Registration<Student>();
I am unable to figure out how to write the declaration for the generics part of the class header. Below I have given the Registration class. To note, the class Student inherits the Person class.
import java.util.List;
import java.util.ArrayList;
public class Registration<T extends Person> //I believe my issue is here{
private T [] roster;
private List<T> waitList;
private static int rosterCount;
private T lastInstance;
public Registration () {
roster = (T[]) new Object[100];
waitList = new ArrayList<>();
rosterCount = 0;
lastInstance = null;
}
public void addIndex (T t) {
if (rosterCount>=100) {
waitList.add(t);
}
else {
roster [rosterCount] = t;
rosterCount++;
}
lastInstance = t;
}
public T getLastInstance() {
return lastInstance;
}
public int getRosterCount() {
return rosterCount;
}
public int getWaitListCount() {
return waitList.size();
}
}
**Taken from Uoft CSC207 Aug2017 Exam
If Registration has to be generic enough to accept any type, eg. String, then you should declare it as:
class Registration<T> {
private T[] roster;
...
public Registration (Class<T> clazz) {
roster = (T[])Array.newInstance(clazz, 100);
}
...
}
The following snippet would do, note that
Registration<Person> r2 = new Registration<Student>();
is not possible so rewritten at the bottom.
static class Person {}
static class Student extends Person {}
static class Registration<T> {
private T[] roster;
private List<T> waitList;
static int rosterCount;
private T lastInstance;
public Registration() {
roster = (T[]) new Object[100]; ;
waitList = new ArrayList<T>();
}
}
public static void main(String[] args) {
Registration<String> r1 = new Registration<String>();
Registration<? extends Person> r2 = new Registration<Student>();
}
The question is wrong or you've copied it incorrectly somehow. There exists no such class declaration that can satisfy this instantiation.
Registration<Person> r = new Registration<Student>();
This is because by placing the object in r you allow its constraints to be violated.
For instance:
Registration<Student> r0 = new Registration<Student>();
r0.addIndex(new Student());
Student s0 = r0.getLastInstance(); // returns a student instance
r0. addIndex(new Person()); // error, person is not a subclass of student
Registration<Person> r1 = r0; // error, but we will pretend it's allowed - you could force it with a cast
r1.addIndex(new Person()); // this is cool, but what happens to the underlying object?
Person p = r1.getLastInstance(); // this is fine, just like before
Student s1 = r0.getLastInstance(); // error, returns a person instance!
This happens because r0 and r1 are the same object and so have the same last instance - a Person instance. But the generic type of r0 promises that it's return type is Student. That is why you cannot assign generics in the same way you can with normal types. This is why you wildcards have their place. It means the generic type can only be used to return things of that type eg. <? extends Person>, but not pass them as parameters. Or it means that the generic type can only be passed things of that type <? super Student>, but not return them.

Java Map with variable generics as values

So here's a slightly tricky question (for me).
I have a generic object. Call it MyObject. This object has a method which returns something of the type T:
public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(Obviously my "MyObject" does a bit more but that's the gist).
Now, I want to have a map of this type:
Map<String, MyObject<?>> m = new HashMap<>();
I want to be able to fetch maps using some predefined string name, and these maps can be of any MyObject. For example, I could call:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
etc.
But - and here's the tricky part - I want the map to "remember" the parameterized type of MyObject when I fetch some value from the map. Using
m.get("map_1");
would return a
MyObject<Object>
type, since the map was defined as containing
MyObject<?>
values. Thus:
m.get("map_1").get() // <-- This is an Object, not a String!
What modification (if any) is possible, in order to be able to get the correct - full - information regarding the MyObject fetched object, such that invoking the last line (m.get("map_1")) would return a
MyObject<String>
Thanks :)
Amir.
Typesafe Heterogeneous Containers from Joshua Bloch's Effective Java might work here. Basically you add a Class object to represent the type.
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
Then you could do something like this:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
Which is safe and will compile, but will throw a runtime error if you get the type wrong.
(Note I didn't actually compile that, so I might have syntax errors floating around. But most folks don't know how to use Class to cast objects.)
You can get the class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
Here is a full test.
public class GenericsTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
The type system only knows about types, not objects, and therefore can not distinguish "key1" from "key2", because both are of type String.
If keys have different types, the easiest way is to encapsulate a weakly typed map, and use reflective casts to prove to the compiler the types are correct:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);

Categories

Resources