Handling of <?> type in Java generics - java

Let's say I have an interface that describes possible request handlers that can service actions requested by a client in the context of some client session state, that is stored by the client:
public interface RequestHandler<State> {
// Perform action in the context of currentState and return updated state
public State request(State currentState, String action);
}
To make it easy to implement the RequestHandlers, I added the generic type State, which encapsulates all the required client session data.
Now, a simple client could look like this:
public class Client {
private final RequestHandler<?> handler;
private Object state;
Client(RequestHandler<?> handler) {
// Initialize the RequestHandler to use for "go"-requests
this.handler = handler;
// Initialize our client state to "null"
this.state = null;
}
public void go() {
// Execute "go"-request in current state and update state
state = handler.request(state, "go"); // <= this is an error (see below)
}
}
During creation it gets provided with a RequestHandler, which it then later uses to execute "go"-requests. It also manages storage of its current session state in the private state variable.
Now, since my clients shouldn't need to worry about what the session state actually looks like internally, I would like to use RequestHandler<?> as shown. But, unfortunately, this gives me an error in the state = handler.request... line:
The method request(capture#3-of ?, String) in the type
RequestHandler is not applicable for the arguments
(Object, String)
Is there an issue with just changing the offending line to:
state = ((RequestHandler<Object>) handler).request(state, "go");
(which turns the error into an "Unchecked cast"-warning)
Clearly, this way I loose type-checking on my state-object, but if the Client only ever sets it to null or something returned by the RequestHandler, there should be no problems, right?
I know I could also just parameterize Client as Client<State> as well and then using State in place of Object and ? everywhere. But I would rather like to avoid this, since it's (in my opinion) just dead weight in this case that will have to be carried around wherever a Client is instantiated or used...
There is no way to cast state to (?), right?
UPDATE:
There's a beautiful solution to this problem if everything was happening inside a single method rather than a class:
public <State> void go(RequestHandler<State> handler) {
State state = null;
state = handler.request(state, "go");
state = handler.request(state, "go again");
state = handler.request(state, "go one more time");
}
This, I can call anywhere without having to always specify what State actually is. But there is no equivalent construct for entire classes (some inferred generic argument) is there?

It seems like it would be okay to make Client generic, reflecting the type of the RequestHandler. But, if you want to hide that inside the client, you can do it like this:
public final class Client
{
private final CaptureHelper<?> helper;
<T> Client(RequestHandler<T> handler) {
this.helper = new CaptureHelper<T>(handler);
}
public void go()
{
helper.request("go");
}
private final class CaptureHelper<T>
{
private final RequestHandler<T> handler;
private T state;
private CaptureHelper(RequestHandler<T> handler) {
this.handler = handler;
}
private void request(String action)
{
state = handler.request(state, action);
}
}
}
Note that this frees anyone using a client from caring about the generic type of its RequestHandler. Users would have code like this:
RequestHandler<?> handler = ... ;
Client client = new Client(handler); /* Look ma, no generics! */
client.go();

It appears that your intention is that clients should be able to define RequestHandlers that consume and return a variety of objects conforming to a State interface. If so, you need to define your interface like this so that the clients can specify the particular State type they're using:
public interface RequestHandler<StateType> {
// Perform action in the context of currentState and return updated state
public StateType request(StateType currentState, String action);
}
If your Client class is intended to be generic (in the non-technical sense), then it should also be generic (in the technical sense) by allowing subclasses or its own clients to specify the type of the State object:
public class Client<StateType> {
private StateType state;
...
}
The problem with your existing code is that there's absolutely no information contained in an absolute wildcard. When the wildcard represents a return type, it might be the case that you're getting an object of class Object, with no useful behavior, and as an argument type, it would basically mean that the client could expect you to supply an object of any specific class but wasn't saying which.

Related

How to use the Java type system to create classes handling network messages, which determine their proper handler using a field in the message?

I have a lot of classes implementing a "common" interface called Setter.
public interface Setter {
Result set(Config config, int entityId);
enum Result {
HANDLED, HANDLING_ERROR, REJECTED
}
}
An example of an implementation looks like this, it sets in the world a 'number' value for a given 'entity' distinguished by its entityId:
public class NumberSetter implements Setter {
private World world;
private Assets assets;
public NumberSetter(World world, Assets assets) {
this.world = world.
this.assets = assets;
}
#Override
public Result set(Config config, int entityId) {
if (config instanceof NumberConfig numberConfig) {
world.passNumber(entityId, numberConfig.number);
return Result.HANDLED;
} else {
return Result.REJECTED;
}
}
}
Please do notice, that the Config object is cast to a specific NumberConfig, otherwise the Setter implementation signals it didn't handle the argument.
I am using a Set of these Setters in a network-enabled class, where it tries to match a super-type Config object against one of these Setters from the Set. (The naming might be subject to change lmao.) The code below handles a network package by passing it to all of the Setters in the Set and checks if there were any errors or if no Setter handled the package. If the check passes then the package wasn't handled properly and the Handler returns a NOT_HANDLED which later crashes the program because I'm still at the development stage.
public class ConfigNetworkHandler implements NetworkHandler {
private final Assets assets;
private final Set<Setter> setterSet;
public ConfigNetworkHandler(
Assets assets,
Set<Setter> setterSet
) {
this.assets = assets;
this.setterSet = setterSet;
}
#Override
public boolean handle(WebSocket webSocket, int worldEntity, Component component) {
var configId = ((ConfigId) component).getId();
var config = assets.getConfigs().get(configId);
var setterResults = setterSet.stream()
.map(setter -> setter.set(config, worldEntity))
.toList();
var anyErrors = setterResults.stream().anyMatch(HANDLING_ERROR::equals);
var wasHandled = setterResults.stream().anyMatch(HANDLED::equals);
if (anyErrors || !wasHandled) {
return NOT_HANDLED;
}
return FULLY_HANDLED;
}
}
I don't like it how I am not using Java's type system properly. I don't know how to do it otherwise, without manually providing a Map between ConfigId's and the Setters, which I would rather not do, because the ConfigIds aren't known at compile-time. The NetworkHandler-type-stuff is kind of similar but there are a lot less of them and they will probably be refactored in a similar way (there is also a lot fewer of them, so it's not a practical issue).
I like the current solution because it allows me to add and remove Setters without worrying about the other ones and also I don't need to change the implementation of ConfigNetworkHandler, because it's provided a Set. I don't like it, because it requires list traversing, doesn't seem "idiomatic" for Java, returns weird Results instead of just not being called because it doesn't accept the type, and FEELS like there should be something else.
Do you have an idea how to approach this differently?

make subclass serialize as an instance of the super class?

im dealing with a codebase that has builds MBeans (for export to jmx).
the original code simply builds an MBeanInfo instance:
#Override
public MBeanInfo getMBeanInfo() {
MBeanAttributeInfo[] attrs = //SLOW TO BUILD
return new MBeanInfo(...attrs...);
}
since the mbean attributes are expensive to build, and this method get called rather frequently (even with no jmx clients attached), i've tried creating a subclass of MBeanInto that lazily calculates those attributes:
public class LazyMBeanInfo extends MBeanInfo implements Externalizable {
private transient AttributeCallback callback = null;
private volatile MBeanAttributeInfo[] lazyAttrs = null;
public LazyMBeanInfo(...AttributeCallback callback...) throws IllegalArgumentException {
super(className, description, null, constructors, operations, notifications);
this.callback = callback;
}
#Override
public MBeanAttributeInfo[] getAttributes() {
MBeanAttributeInfo[] val = lazyAttrs;
if (val != null) {
return val.clone(); //match upstream behaviour
}
if (callback == null) {
throw new IllegalStateException("BUG");
}
val = callback.buildAttributes();
if (val == null) {
val = new MBeanAttributeInfo[0];
}
lazyAttrs = val;
return val.clone();
}
public interface AttributeCallback {
MBeanAttributeInfo[] buildAttributes();
}
}
the problem is that JMX (over RMI) serializes the MBeanInfo object, and then in jconsole (or jvisualVM) i get an error:
so - can i somehow implement Externalizable and serialize myself as an instance of the parent class? ideally i'd like this to work:
public class LazyMBeanInfo extends MBeanInfo implements Externalizable {
//same as before, plus:
#Override
public void writeExternal(ObjectOutput out) throws IOException {
MBeanInfo vanilla = new MBeanInfo(...);
out.writeObject(vanilla);
}
}
but it doesnt.
is this possible somehow ?
Unless you are using [highly dynamic] DynamicMBeans, I don't see why the MBeanInfo needs to be rebuilt for every call to getMBeanInfo(), but ....
Your LazyMBeanInfo can be made to work (though I have not tested this specific case). MBeanInfo already implements Serializable, so what you want is for the serialization process to write out an MBeanInfo, not a LazyMBeanInfo, since the client probably doesn't have that class in its classpath. However, LazyMBeanInfo can implement this method:
Object writeReplace() throws ObjectStreamException;
at which point you write out the underlying MBeanInfo. See the Serializable JavaDoc, specifically:
Serializable classes that need to designate an alternative object to
be used when writing an object to the stream should implement this
special method with the exact signature:
ANY-ACCESS-MODIFIER Object writeReplace() throws
ObjectStreamException;
In that way, the actual object can be an instance of LazyMBeanInfo, but what you write out can be an actual MBeanInfo, built from your cached lazyAttrs.
Having said that, rather than implementing a build-on-first-call approach, I would implement a build-before-first-use by simply building the full MBeanInfo when the MBean is first created, or when the MBean is registered. Then just return the pre-built MBeanInfo on each getMBeanInfo() call.
To do this at MBean registration time, implement the MBeanRegistration interface, and build the cached MBeanInfo in the postRegister method.

Observer Pattern - Update method parameter

I am trying to use the properties of the object that is passed into the notifyObservers method, but I can't seem to find a way to access them. I can only pass in a single string, but I want more options to be passed into the observer.
This is a sample of the setup;
public class UpdateInfo {
public String data;
public int debug = 0;
}
public class RXTXComms extends Observable {
UpdateInfo info = new UpdateInfo();
public void sendToBoard(String s) {
.......
UpdateInfo.data = "test";
UpdateInfo.debug = 1;
stChanged();
notifyObservers(info);
}
}
public class Program implements Observer {
public void update(Observable obj, Object arg) {
String msg = ""; // Message to display on GUI
if (arg instanceof UpdateInfo) {
//Print out the message and Debug int onto GUI...but how do I access them from `arg`
}
}
}
If I make the type of arg to be UpdateInfo, then I get a compiler error that my class Program is not abstract....
Is this not an honest, appropriate question?
You need to cast the class.
UpdateInfo ui = (UpdateInfo) arg;
inside your instanceof should do the trick.
The Observable/Observer API of Java is really badly designed, don't use it. Seriously - it should not have been an abstract class, but an interface.
The observer pattern is so simple, just implement it on your own with full type safety. It actually does not pay off to even specify a better interfaced version of it. The various Listeners for example are just another instance of this pattern in Java that is much better executed: the listeners have methods with good method names and extra data, and there exists an abstract Adapter if there is more than one method to implement and you will often not need all of them (see e.g. MouseAdapter).
So re-implement the pattern for a concrete use case, not for the unspecified "if anything happens" case. Patterns are actually meant to be reimplemented, not abstractly inherited from a pattern library.

Understanding best use of Java Generics in this example case

Let's say I have a manufacturing scheduling system, which is made up of four parts:
There are factories that can manufacture a certain type of product and know if they are busy:
interface Factory<ProductType> {
void buildProduct(ProductType product);
boolean isBusy();
}
There is a set of different products, which (among other things) know in which factory they are built:
interface Product<ActualProductType extends Product<ActualProductType>> {
Factory<ActualProductType> getFactory();
}
Then there is an ordering system that can generate requests for products to be built:
interface OrderSystem {
Product<?> getNextProduct();
}
Finally, there's a dispatcher that grabs the orders and maintains a work-queue for each factory:
class Dispatcher {
Map<Factory<?>, Queue<Product<?>>> workQueues
= new HashMap<Factory<?>, Queue<Product<?>>>();
public void addNextOrder(OrderSystem orderSystem) {
Product<?> nextProduct = orderSystem.getNextProduct();
workQueues.get(nextProduct.getFactory()).add(nextProduct);
}
public void assignWork() {
for (Factory<?> factory: workQueues.keySet())
if (!factory.isBusy())
factory.buildProduct(workQueues.get(factory).poll());
}
}
Disclaimer: This code is merely an example and has several bugs (check if factory exists as a key in workQueues missing, ...) and is highly non-optimal (could iterate over entryset instead of keyset, ...)
Now the question:
The last line in the Dispatcher (factory.buildProduct(workqueues.get(factory).poll());) throws this compile-error:
The method buildProduct(capture#5-of ?) in the type Factory<capture#5-of ?> is not applicable for the arguments (Product<capture#7-of ?>)
I've been racking my brain over how to fix this in a type-safe way, but my Generics-skills have failed me here...
Changing it to the following, for example, doesn't help either:
public void assignWork() {
for (Factory<?> factory: workQueues.keySet())
if (!factory.isBusy()) {
Product<?> product = workQueues.get(factory).poll();
product.getFactory().buildProduct(product);
}
}
Even though in this case it should be clear that this is ok...
I guess I could add a "buildMe()" function to every Product that calls factory.buildProduct(this), but I have a hard time believing that this should be my most elegant solution.
Any ideas?
EDIT:
A quick example for an implementation of Product and Factory:
class Widget implements Product<Widget> {
public String color;
#Override
public Factory<Widget> getFactory() {
return WidgetFactory.INSTANCE;
}
}
class WidgetFactory implements Factory<Widget> {
static final INSTANCE = new WidgetFactory();
#Override
public void buildProduct(Widget product) {
// Build the widget of the given color (product.color)
}
#Override
public boolean isBusy() {
return false; // It's really quick to make this widget
}
}
Your code is weird.
Your problem is that you are passing A Product<?> to a method which expects a ProductType which is actually T.
Also I have no idea what Product is as you don't mention its definition in the OP.
You need to pass a Product<?> to work. I don't know where you will get it as I can not understand what you are trying to do with your code
Map<Factory<?>, Queue<Product<?>>> workQueues = new HashMap<Factory<?>, Queue<Product<?>>>();
// factory has the type "Factory of ?"
for (Factory<?> factory: workqueues.keySet())
// the queue is of type "Queue of Product of ?"
Queue<Product<?>> q = workqueues.get(factory);
// thus you put a "Product of ?" into a method that expects a "?"
// the compiler can't do anything with that.
factory.buildProduct(q.poll());
}
Got it! Thanks to meriton who answered this version of the question:
How to replace run-time instanceof check with compile-time generics validation
I need to baby-step the compiler through the product.getFactory().buildProduct(product)-part by doing this in a separate generic function. Here are the changes that I needed to make to the code to get it to work (what a mess):
Be more specific about the OrderSystem:
interface OrderSystem {
<ProductType extends Product<ProductType>> ProductType getNextProduct();
}
Define my own, more strongly typed queue to hold the products:
#SuppressWarnings("serial")
class MyQueue<T extends Product<T>> extends LinkedList<T> {};
And finally, changing the Dispatcher to this beast:
class Dispatcher {
Map<Factory<?>, MyQueue<?>> workQueues = new HashMap<Factory<?>, MyQueue<?>>();
#SuppressWarnings("unchecked")
public <ProductType extends Product<ProductType>> void addNextOrder(OrderSystem orderSystem) {
ProductType nextProduct = orderSystem.getNextProduct();
MyQueue<ProductType> myQueue = (MyQueue<ProductType>) workQueues.get(nextProduct.getFactory());
myQueue.add(nextProduct);
}
public void assignWork() {
for (Factory<?> factory: workQueues.keySet())
if (!factory.isBusy())
buildProduct(workQueues.get(factory).poll());
}
public <ProductType extends Product<ProductType>> void buildProduct(ProductType product) {
product.getFactory().buildProduct(product);
}
}
Notice all the generic functions, especially the last one. Also notice, that I can NOT inline this function back into my for loop as I did in the original question.
Also note, that the #SuppressWarnings("unchecked") annotation on the addNextOrder() function is needed for the typecast of the queue, not some Product object. Since I only call "add" on this queue, which, after compilation and type-erasure, stores all elements simply as objects, this should not result in any run-time casting exceptions, ever. (Please do correct me if this is wrong!)

How to observe when a custom object changes even after it's extended

So let's say I have a custom object in java:
public class TestObject {
private int value = 0;
public TestObject(int value) {
this.value = value;
}
public void increaseValue() {
value++;
}
}
Now I want to know when this object is modified. Or more specifically, I want to know when any of it's fields have changed (in this case value). Furthermore, if I extend TestObject, I still want to be able to listen to any field changes that might happen to that object, including if that change is to a new field new fields.
I've done some research and found of variety of listeners that come with java, but they all seem to fail in the area that they require you to put calls to the listeners at the end of you're methods. For example, increaseValue() would also have to notify all of the listeners to the TestObject that value had changed. Obviously this doesn't work for me because extensibility is a must have and I do not know that if people who inherit from that object will adhere to the requirement that they must notify listeners. Not to mention, it seems like a pain to have to program that for each mutator method
If any light could be shed on this subject it would be greatly appreciated.
You can use approach that Hibernate uses i.e. instrument/proxy your classes to add additional (listener) logic around your POJOs' getters and setters. But then you need to ensure that all use only proxied classes.
Not to mention, it seems like a pain to have to program that for each
mutator method
Take a look at The AspectJ Project. You could achieve this very easily with production aspect using a pointcut for field assignment.
public privileged aspect TestObjectObserver {
before(Object o) : set(* TestObject.*) && args(o) {
// notify listeners
}
}
// runs before field assignment to any field of TestObject. The value to be
// assigned is converted to an object type (int to Integer, for
// example) and named o in the body
// the aspect needs to be declared privileged so access private fields
Obviously this doesn't work for me because extensibility is a must
have and I do not know that if people who inherit from that object
will adhere to the requirement that they must notify listeners
Your approach is not in the correct path.
You can not enforce a derived class to keep the base's class invariants since inheritance can allow descendant classes to alter implementation in a way that makes them invalid from the viewpoint of the parent class.
In your case the derived class MAY or MAY not call the notification upon modification.
In cases like this you should model arround Composition and not Inheritence
Composition vs Inheritence
For your example:
class Value{
private int value;
public void increaseValue() {
value++;
}
}
Value knows how to increment itself.
class ValueHolder extends Observable {
private Value theValue;
public ValueHolder(Value v){
theValue = v;
}
public void modifyValue(String methodName)(){
Method method = theValue.getClass().getMethod(methodName, null);
method.invoke(theValue,null);//Since it has no args
setChanged();
notifyObservers();
}
}
So all the code will be as follows:
ValueHolder vh = new ValueHolder(new Value(10));
//registration of listeners
vh.modifyValue("increaseValue");
//So if you extend the Value
class DerivedValue extends Value{
private int y;
public void increaseY() {
y++;
}
}
ValueHolder vh = new ValueHolder(new DerivedValue());
//registration of listeners
vh.modifyValue("increaseY);
So the catch now is that the usage of the objects are via the holder.
Then the notification will happen.

Categories

Resources