How i can check if a class is of a determinated type for example:
// PacketHandler.java
public interface PacketHandler<T> {
public void handlePacket(T packet);
}
// PacketReader.java
public void read() {
Packet packet = // Read some input
for(PacketHandler packetHandler : packetHandlers) {
if (packetHandler is the type of packet) { // here is the problem
packetHandler.handlePacket(packet);
}
}
}
public void registerHandler(PacketHandler<?> packetHandler) {
packetHandlers.add(packetHandler);
}
// Main
packetReader.registerHandler(new PacketHandler<RandomPacket>() {
public void handlePacket(RandomPacket packet) {
// I handle the packet
}
});
I know that this question maybe seems stupid;
but how to solve this problem?
**Edit*****
Jon Skeet, so the class should be:
public class RandomClass implements PacketHandler {
public boolean handlePacket(Packet packet) {
if (packet instanceof PacketThatThisClassHandle) {
//handle with casting
return true;
} else {
return false;
}
}
}
Unfortunately Java generics use type erasure, meaning that at execution time, any particular PacketHandler<T> is just PacketHandler as far as the VM is concerned.
You may want to change your code to:
public interface PacketHandler {
// The parameter type can be Object if you really want
boolean tryHandlePacket(Packet packet);
}
... and make a PacketHandler just return false if it doesn't know how to handle a particular packet type.
Then you can just use:
for (PacketHandler handler : handlers) {
if (handler.tryHandlePacket(packet)) {
break;
}
}
(That's assuming you only want a single handler to actually handle any packet type.)
If you still want a generic interface, you'd either need a boolean handlesPacket(Packet) method, or possibly a Class<T> getPacketType() method. Either way it's still going to be a pain in terms of casting the packet to the right type...
If you have lots of packet handlers, you could potentially create an abstract base class:
public abstract class AbstractPacketHandler<T extends Packet>
implements PacketHandler {
private final Class<T> packetType;
protected AbstractPacketHandler(Class<T> packetType) {
this.packetType = packetType;
}
protected abstract void handlePacket(T packet);
public boolean tryHandlePacket(Packet packet) {
if (!packetType.isInstance(packet)) {
return false;
}
handlePacket(packetType.cast(packet));
return true;
}
}
Then you can write:
public class FooPacketHandler extends PacketHandler<Foo> {
public FooPacketHandler() {
super(Foo.class);
}
protected void handlePacket(Foo packet) {
...
}
}
Type erasure won't make this attempt easy. The mapping part it's quite easy, you can use a HashMap. But the problem is that the handlePacket method accepts a parameter of type T, which forces you to cast the object to that type before passing it to the handler.
To avoid relaxing the constraint you could use a two level invokation, something like:
interface Packet { }
class ConcretePacket implements Packet { }
HashMap<Class<?>, PacketHandler<?>> mapping =
new HashMap<Class<?>, PacketHandler<?>>();
public abstract class PacketHandler<T extends Packet> {
PacketHandler(Class<T> clazz) {
mapping.put(clazz, this);
}
public final void handlePacket(Packet packet) {
doHandlePacket((T)packet);
}
public abstract void doHandlePacket(T packet);
}
public class ConcretePacketHandler extends PacketHandler<ConcretePacket>
{
ConcretePacketHandler()
{
super(ConcretePacket.class);
}
public void doHandlePacket(ConcretePacket s) {
// whatever
}
}
public void receivedPacket(Packet packet) {
PacketHandler<?> handler = mapping.get(packet.getClass());
if (handler != null)
handler.handlePacket(packet);
}
Mind that this could not work in certain situations (maybe with different classloaders involved) and that, to manage subclasses of PacketManager, you will need to find a better way to retrieve the correct handler, eg by using a List<Pair<Class<?>,PacketHandler<?>>> so that you can check
if (listElement.clazz.isAssignableFrom(packet.getClass()))
handler = listElement.handler;
maybe even using priorities so that the exact class is found before a possible superclass.
I don't exactly understand what you are trying to get but you could possibly use:
if ( type.isInstance(obj) ) {
//do something
}
http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#isInstance%28java.lang.Object%29
Related
I have an application that takes json objects from a queue, deserializes them to a model, applies a list of filters, and sends the objects that pass all filters through to another queue.
The two complicating criteria are:
The set of filters is determined and injected via Spring profile at startup.
The type of object that the json is being deserialized to is also determined the by the Spring profile at startup.
The following solution is ugly because it involves casting:
public class MessageTypeOne {
public int someField;
}
public class MessageTypeTwo {
public int otherField;
}
public interface MessageFilter {
boolean doesFilterPass(Object object);
}
#Component
#Profile("ProfileOne")
public class OneOfMyMessageFilters implements MessageFilter {
public boolean doesFilterPass(Object object) {
MessageTypeOne message = (MessageTypeOne)object;
if (message.someField == something) {
return false;
} else return true;
}
}
#Component
#Profile("ProfileTwo")
public class AnotherOneOfMyMessageFilters implements MessageFilter {
public boolean doesFilterPass(Object object) {
MessageTypeTwo message = (MessageTypeTwo)object;
if (message.otherField == something) {
return false;
} else return true;
}
}
#Service
public class MessageFilterService {
// injected at runtime via Spring profile
private Set<MessageFilter> messageFilters
#AutoWired
public MessageFilterService(Set<MessageFilter> messageFilters) {
this.messageFilters = messageFilters;
}
public boolean passesAllFilters(Object object) throws IOException {
for (MessageFilter filter : messageFilters) {
if (!filter.doesFilterPass(object)) {
return false;
}
}
return true;
}
}
What's the cleanest pattern for cases like these? I've read about the visitor pattern but I'm not sure that's any better than casting like this.
As far as design pattern is concerned, I think it is of type Strategy pattern. I am not talking about Spring way of implementation. You may have n number of filters, but you have to choose based upon the context. So strategy pattern is best fitted here. Others can provide other patterns. You can strategy pattern in the below link.
https://en.wikipedia.org/wiki/Strategy_pattern
What about visitor pattern with Java reflection? Here is an old article:
https://www.javaworld.com/article/2077602/java-tip-98--reflect-on-the-visitor-design-pattern.html
When you want to decouple messages from filters and relation is many to many you can always use Chain of Responsibility.
#Service
public class MessageFiltersAggregator {
private MessageFilter chainEntryNode;
#AutoWired
public MessageFilterService(Set<MessageFilter> messageFilters) {
this.chainEntryNode = buildChain(messageFilters);
}
public boolean passesAllFilters(Object object) throws IOException {
return chainEntryNode.doesFilterPass(object);
}
}
You need to implement buildChain method which creates chain from collection. Of course, each element in chain should have next property. In this case MessageFilter could look like below:
public abstract class MessageFilter {
private MessageFilter next;
//constructors, setters, etc
public boolean doesFilterPass(Object object) {
boolean res = true;
if (canHandle(object)) {
res = validate(object);
}
return res && next.doesFilterPass(object);
}
public abstract boolean validate(Object object);
public abstract boolean canHandle(Object object);
}
Abstract class contains chain logic you just need to implement two methods in each subclass. One of implementation could look like below:
public class AnotherOneOfMyMessageFilters extends MessageFilter {
public boolean canHandle(Object object) {
return object instanceof MessageTypeTwo;
}
public boolean validate(Object object) {
MessageTypeTwo message = (MessageTypeTwo)object;
return message.otherField == something;
}
}
All above classes are just example created without IDE so could have issues in syntax but should give you an idea how it should work.
See also:
Chain of Responsibility in Java
Chain of Responsibility Design Pattern in Java
If I understand your problem correctly, then it's possible to configure your Spring profile in a way that makes your filters throw ClassCastExceptions.
Assuming that you configuration options are the way you want, then it demonstrates the only real problem with your design -- your filters can be applied to any Object, and that's what the interface says -- doesFilterPass( Object ) -- but your filters only really work with certain types of objects.
That's what you need to fix. If the filter is applied to a strange type of object, does it pass or fail? You can decide this on a per-filter basis and then just fix it like this:
public boolean doesFilterPass(Object object) {
if (!(object instanceOf MessageTypeTwo)) {
return true;
}
MessageTypeTwo message = (MessageTypeTwo)object;
if (message.otherField == something) {
return false;
} else return true;
}
Easy peasy.
I know you don't like the cast, but it's a direct result of the configuration options you provide -- the profile can be configured to apply filters to any kind of object. You just need to support that, and that means there has to be casting somewhere.
This became much cleaner with generics. Since I know what type of Object each filter can handle I can just do this, eliminating the casting:
public class MessageTypeOne {
public int someField;
}
public class MessageTypeTwo {
public int otherField;
}
public interface MessageFilter<T> {
boolean doesFilterPass(T message);
}
#Component
#Profile("ProfileOne")
public class OneOfMyMessageFilters<T extends MessageTypeOne> implements MessageFilter<T> {
public boolean doesFilterPass(MessageTypeOne message) {
if (message.someField == something) {
return false;
} else return true;
}
}
#Component
#Profile("ProfileTwo")
public class AnotherOneOfMyMessageFilters<T extends MessageTypeTwo> implements MessageFilter<T> {
public boolean doesFilterPass(MessageTypeTwo message) {
if (message.otherField == something) {
return false;
} else return true;
}
}
#Service
public class MessageFilterServiceImpl<T> implements MessageFilterService<T> {
// injected at runtime via Spring profile
private Set<MessageFilter<T>> messageFilters
#AutoWired
public MessageFilterService(Set<MessageFilter<T>> messageFilters) {
this.messageFilters = messageFilters;
}
public boolean passesAllFilters(T message) throws IOException {
for (MessageFilter filter : messageFilters) {
if (!filter.doesFilterPass(message)) {
return false;
}
}
return true;
}
}
public interface MessageFilterService<T> {
boolean passesAllFilters(T rawEvent) throws IllegalArgumentException;
}
Is there a better way to deal with an instanciation of an object (Product) which depends upon another object type (Condition) than using if-else paired with instanceof as the following code shows?
import java.util.ArrayList;
import java.util.List;
abstract class AbstractProduct {
private AbstractCondition condition;
public AbstractProduct(AbstractCondition condition) {
this.condition = condition;
}
public abstract void doSomething();
}
class ProductA extends AbstractProduct {
AbstractCondition condition;
public ProductA(AbstractCondition condition) {
super(condition);
}
#Override
public void doSomething() {
System.out.println("I'm Product A");
}
}
class ProductB extends AbstractProduct {
public ProductB(AbstractCondition condition) {
super(condition);
}
#Override
public void doSomething() {
System.out.println("I'm Product B");
}
}
class AbstractCondition { }
class ConditionA extends AbstractCondition { }
class ConditionB extends AbstractCondition { }
public class Try {
public static void main(String[] args) {
List<AbstractCondition> conditions = new ArrayList<AbstractCondition>();
List<AbstractProduct> products = new ArrayList<AbstractProduct>();
conditions.add(new ConditionA());
conditions.add(new ConditionB());
conditions.add(new ConditionB());
conditions.add(new ConditionA());
for (AbstractCondition c : conditions) {
tryDoSomething(c);
}
}
public static void tryDoSomething(AbstractCondition condition) {
AbstractProduct product = null;
if (condition instanceof ConditionA) {
product = new ProductA(condition);
} else if (condition instanceof ConditionB) {
product = new ProductB(condition);
}
product.doSomething();
}
}
The difference with the code above of my real code is: I have NO direct control over AbstractCondition and its subtypes (as they are in a library), but the creation of a concrete subtype of AbstractProduct depends on the concrete condition.
My goal being: try to avoid the if-else code smell in tryDoSomething().
I would also like to avoid reflection because it feels like cheating and I do think it's not an elegant, clean and readable solution.
In other words, I would like to tackle the problem just with good OOP principles (e.g. exploiting polymorphism) and pheraps some design patterns (which apparently I don't know in this specific case).
Since you can't edit the original objects, you need to create a static map from condition type to product type:
private static HashMap< Class<? extends AbstractCondition>,
Class<? extends AbstractProduct>
> conditionToProduct;`
Fill it in static initialization with the pairs of Condition,Product:
static {
conditionToProduct.put(ConditionA.class, ProductA.class);
...
}
and in runtime just query the map:
Class<? extends AbstractProduct> productClass = conditionToProduct.get(condition.getClass());
productClass.newInstance();
AbstractCondition needs to know either the type or how to construct a product.
So add one of the following functions to AbstractCondition
Class<? extends AbstractProduct> getProductClass()
or
AbstractProduct createProduct()
You should create a Factory class to help you with that then.
interface IFactoryProduct{
AbstractProduct getProduct(AbstractCondition condition) throws Exception;
}
This will be your interface, just need to implement it like this.
class FactoryProduct implements IFactoryProduct{
public AbstractProduct getProduct(AbstractCondition condition) throws Exception{
return (AbstractProduct)getClass().getMethod("getProduct", condition.getClass()).invoke(this, condition);
}
public ProductA getProduct(ConditionA condition){
return new ProductA();
}
public ProductB getProduct(ConditionB condition){
return new ProductB();
}
}
Using the reflexion to redirect with the correct method will do the trick. this is upgradable for subclassed if you want.
EDIT:
Some example :
List<AbstractCondition> list = new ArrayList<AbstractCondition>();
list.add(new ConditionA());
list.add(new ConditionB());
for(AbstractCondition c : list){
try {
System.out.println(f.getProduct(c));
} catch (Exception ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
labo.ProductA#c17164
labo.ProductB#1fb8ee3
A more complexe reflexion version allowing a subclass to be received :
public AbstractProduct getProduct(AbstractCondition condition) throws Exception{
Method m = getMethodFor(condition.getClass());
if(m == null )
throw new Exception("No method for this condition " + condition.getClass().getSimpleName());
else
return (AbstractProduct) m.invoke(this, condition);
}
private Method getMethodFor(Class<? extends AbstractCondition> clazz ) throws Exception{
try {
return getClass().getMethod("getProduct", clazz);
} catch (NoSuchMethodException ex) {
if(clazz.getSuperclass() != AbstractCondition.class){
return getMethodFor((Class<? extends AbstractCondition>)clazz.getSuperclass());
}
return null;
}
}
This allows me to send ConditionC extending ConditionB to build the same product has ConditionB would. Interesting for complexe heritage.
Given the following abstract class:
public abstract class BaseVersionResponse<T extends BaseVO> {
public abstract void populate(T versionVO);
}
and the following child class:
public class VersionResponseV1 extends BaseVersionResponse<VersionVOV1>
{
protected String testFieldOne;
protected String testFieldTwo;
public String getTestFieldOne() {
return testFieldOne;
}
public void setTestFieldOne(String value) {
this.testFieldOne = value;
}
public String getTestFieldTwo() {
return testFieldTwo;
}
public void setTestFieldTwo(String value) {
this.testFieldTwo = value;
}
#Override
public void populate(VersionVOV1 versionVO) {
this.setTestFieldOne(versionVO.getFieldOne());
this.setTestFieldTwo(versionVO.getFieldTwo());
}
I desire to do something like this from a calling method:
public void getVersionInfo(String version) {
BaseVO versionVO = null;
BaseVersionResponse<? extends BaseVO> baseVersionResponse = null;
baseVersionResponse = createVersionResponse(version);
versionVO = createVersionVO(version);
baseVersionResponse.populate(versionVO);
}
where createVersionResponse(...) and createVersionVO(...) look like this:
public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version) {
BaseVersionResponse<? extends BaseVO> specificVersionResponse = null;
if (version.equalsIgnoreCase("V1")) {
specificVersionResponse = new VersionResponseV1();
} else if (version.equalsIgnoreCase("V2"))
specificVersionResponse = new VersionResponseV2();
return specificVersionResponse;
}
public BaseVO createVersionVO(String version) {
BaseVO versionVO = null;
if (version.equalsIgnoreCase("V1")) {
versionVO = new VersionVOV1();
} else if (version.equalsIgnoreCase("V2"))
versionVO = new VersionVOV2();
return versionVO;
}
and VersionVOV1 looks like this:
public class VersionVOV1 extends BaseVO {
private String fieldOne = null;
private String fieldTwo = null;
private String fieldThree = null;
public String getFieldOne() {
return fieldOne;
}
public void setFieldOne(String fieldOne) {
this.fieldOne = fieldOne;
}
public String getFieldTwo() {
return fieldTwo;
}
public void setFieldTwo(String fieldTwo) {
this.fieldTwo = fieldTwo;
}
public String getFieldThree() {
return fieldThree;
}
public void setFieldThree(String fieldThree) {
this.fieldThree = fieldThree;
}
}
My problem arises when I try to compile this line of code:
baseVersionResponse.populate(versionVO);
in getVersionInfo(...). I'm getting a message that looks like this:
The method populate(capture#3-of ?) in the type BaseVersionResponse is not applicable for the arguments (BaseVO)
on the populate method above.
My thought was (which is apparently incorrect) that since the baseVersionResponse is, at this point in the code, actually a specific child instance, that the class would know exactly which populate method to call from that specific child class.
What am I doing wrong here? Is there a better way to do this if this isn't the correct approach?
Thank you for your time!
Ok, I took a better look at this today. The problem is that the wildcard, while the right way to go, precludes you from doing:
BaseVO versionVO = createVersionVO(version);
Because the populate call wants an extension of BaseVO, not an actual BaseVO, which doesn't qualify. That means you can't pass that versionVO variable directly.
So, to keep the type checking in place, which I think is good because you'll always want an implementation, leave pretty much everything as-is above, and change your BaseVersionResponse class to something like:
public abstract class BaseVersionResponse<T extends BaseVO> {
public T getVersion(BaseVO versionVO) {
try {
return (T) versionVO;
} catch (ClassCastException e) {
throw new IllegalArgumentException();
}
}
public abstract void populate(BaseVO versionVO);
}
So, populate method now takes a BaseVO, and there's a new getVersion method to do some explicit casting for us. This should be ok since we know that the factory will always supply the right thing, but if another caller doesn't, an IllegalArgumentException is thrown.
Now, in your response class implementation, change the populate method accordingly:
public void populate(BaseVO version) {
VersionVOV1 versionVO = getVersion(version);
this.setTestFieldOne(versionVO.getFieldOne());
this.setTestFieldTwo(versionVO.getFieldTwo());
}
So, we've changed the populate method to take BaseVO, and the getVersion method does the casting for us. All the other type checks still apply, and we're good to go.
The casting makes it feel not as clean, but for the factory approach you're using, it's really the only way (I can think of) to keep the guarantees made by the type declarations and the code pattern in tact.
Hope that helps!
If you just take out the capture of type (the "<?>"), and leave it unchecked, it should work just fine. Even using type Object would have compiled.
But, given your specific example, what you probably want is the method:
public BaseVersionResponse<?> createVersionResponse(String version)
Changed to:
public BaseVersionResponse<? extends BaseVO> createVersionResponse(String version)
Then, instead of using
BaseVersionResponse<?>
use
BaseVersionResponse<? extends BaseVO>
Since you know that the return type will be one of those things that implements the interface/class.
I am working with a simple, binary protocol. Each packet consists of 10 bytes. The first byte specifies the packet type. There are many (~50) packet types used.
I want to write a general parser for this protocol which is independent of the handling of packets. So the parser should detect the packet type and put the data into an instance of the appropriate packet class, which holds the protocol data. E.g., considering the classes below: When parser detects packet type 1 --> new Type1() and read raw bytes and set temperature and humidity. Similarly for packet type 2 and all the other packet types.
class Packet {
byte[] raw;
}
class Type1 extends Packet {
int temperature;
int humidity;
}
class Type2 extends Packet {
DateTime sunrise;
DateTime sunset;
}
Since there are so many packet types but each application only uses very few, it should be possible to register for certain types before parsing starts. All other packets types are ignored.
I am planning to have a PacketParser for each packet type. Probably, I need a handler class for each type as well. E.g.:
abstract class Type1Parser {
abstract void handle(Type1 packet);
}
class Type1Parser extends PacketParser {
//how to use/set handler? how to pass packet to handler?
static public Type1Handler type1Handler = null;
#override
void parse(Packet input) {
if(type1Handler == null)
return;
Type1 packet = new Type1(input);
packet.temperature = byteToInt(input.raw, 0, 3);
packet.humidity = byteToInt(input.raw, 4, 7);
type1Handler.handle(packet);
}
}
How to connect parser and handler? Above a naive approach:
The program needs to implement Type1Handler and set the static variable Type1Parser.type1Handler.
Then the main parser can look like this:
class MainParser {
Type1Parser type1 = new Type1Parser();
Type2Parser type2 = new Type2Parser();
...
void parse(byte[] packet) {
switch(packet[0]) {
case 1: type1.parse(packet); break;
case 2: type2.parse(packet); break;
...
}
}
}
However, this seems to be 1) a lot of very similar lines of code 2) a lot of overhead, since all packet parser are instantiated and for each packet parse() is called, even if no handler is registered.
Any ideas how to improve this code?
Note: The parsing should be transparent to the program. Parsing code should stay inside the "parsing library". So ideally, the program only "knows" classes TypeXHandler and TypeX.
There is no perfect answer to this design question, and I don't wish to pretend that mine is, but hopefully my instinctual approach to this problem teaches you things you didn't already know! The main missing component from your code that I see is Generics:
public interface Parser<T extends Packet> {
T parse(Packet packet);
}
public interface Handler<T extends Packet> {
void handle(T packet);
}
This way, you can use lazy static initialization to manage which packet types you are aware of. I won't flesh out the code entirely here, but to give you an idea:
public class TypeRegistry {
private static Map<Integer, TypeHandlerBundle<?>> typeHandlerBundles;
static <T> register(int typeNum, Class<T> clazz, Parser<T> parser, Handler<T> handler) {
// Make bundle, add to map
}
... void parse(Packet packet) {
if (typeHandlerBundles.containsKey((int) packet[0])) {
TypeHandlerBundle<?> bundle = typeHandlerBundles.get((int) packet[0]);
bundle.parseAndHandle(packet);
}
}
}
public class TypeHandlerBundle<T extends Packet> {
...
private final Parser<T> parser;
private final Handler<T> handler;
... void parseAndHandle(Packet packet) {
T parsedPacket = parser.parse(packet);
handler.handle(parsedPacket);
}
}
...
public class Type1Processor {
static {
TypeRegistry.register(1, Type1.class, TYPE1_PARSER, TYPE1_HANDLER);
}
// Definition of constants, implementation, etc.
// ...
}
===
Things I omitted: Qualifiers, lower level implementation, Error-checking, Synchronization, main method, etc. Depending on your set-up, static initialization might not be the right way to call TypeRegistry.register, so you could instead consider a properties file that lists the classes (ugh, but has its merits), or a hard-coded sequence of calls in your main method.
Since Parser and Handler are functional interfaces here, don't forget that you can implement them with lambdas! You can save tons of lines of code that way.
You were right when you said that need one abstract class for parsing array of data.
package parser;
public abstract class TypeParser {
public abstract void parse(byte[] arr);
}
Then for every packet type( you said that you can have 50 but if the first byte indicates the type of packet then 256 deferent types are possible), you can create class as you need for certain type eg.. Type1Parser for type 1 Type122Parser for type 122.
package parser.type;
import parser.TypeParser;
public class Type1Parser extends TypeParser{
public void parse(byte[] array){
// do with the bytes of array what you want
}
}
package parser.type;
import parser.TypeParser;
public class Type122Parser extends TypeParser {
public void parse(byte[] arr) {}
}
Then you can have one class that represents the main parser for all. If you need for every income packet to have one object for later use then you can hold it in vector.
package parser;
import java.util.Vector;
public class MainParser {
private Vector<TypeParser> vecTypeParse=new Vector<TypeParser>();
public void parsePacket(byte[] array){
if(array==null || array.length<1) return; // or throw some exception
int typePacket=array[0]&0xff;
String s="parser.type.Type"+String.valueOf(typePacket)+"Parser";
TypeParser type=null;
try {
type=(TypeParser)Class.forName(s).newInstance(); //here you create class that you need
} catch(InstantiationException e) {e.printStackTrace();
} catch(IllegalAccessException e) {e.printStackTrace();
} catch(ClassNotFoundException e) {e.printStackTrace();}
// you can do something with the exceptons
if(type==null) return; // or throw some exception
type.parse(array); // here parse data for class you just created.
this.vecTypeParse.addElement(type);
}
}
Well, almost like torquestomp answer, here comes my code:
interface Packet {
}
interface PacketParser<T extends Packet> {
Class<T> getPacketClass();
int getPacketId();
int getPacketLength();
Packet parse(byte[] raw, int offset);
}
interface PacketListener<T extends Packet> {
Class<T> getPacketClass();
void onPacket(T packet);
}
interface PacketParsersRegistry {
<T extends Packet> void registerPacketParser(PacketParser<T> packetParser);
<T extends Packet> void registerPacketListener(PacketListener<T> packetListener);
}
class PacketHandlers<T extends Packet> {
final PacketParser<T> parser;
PacketListener<T> listener;
PacketHandlers(PacketParser<T> parser) {
this.parser = parser;
}
void setListener(PacketListener<T> listener) {
this.listener = listener;
}
}
class MainParser implements PacketParsersRegistry {
private final HashMap<Class<?>, PacketHandlers<?>> handlers = new HashMap<>();
private final HashMap<Integer, PacketParser> parsers = new HashMap<>();
#Override
public <T extends Packet> void registerPacketParser(PacketParser<T> packetParser) {
parsers.put(packetParser.getPacketId(), packetParser);
Class<T> packetClass = packetParser.getPacketClass();
handlers.put(packetClass, new PacketHandlers<>(packetParser));
}
#Override
public <T extends Packet> void registerPacketListener(PacketListener<T> packetListener) {
//noinspection unchecked
PacketHandlers<T> handlers = (PacketHandlers<T>) this.handlers.get(packetListener.getPacketClass());
if (handlers != null) {
handlers.setListener(packetListener);
}
}
void parse(byte[] stream, int offset) {
while (offset < stream.length) {
int type = stream[offset];
PacketParser parser = parsers.get(type);
// parser m.b. != null here
PacketListener listener = (PacketListener) handlers.get(parser.getPacketClass());
if (listener != null) {
Packet packet = parser.parse(stream, offset);
//noinspection unchecked
listener.onPacket(packet);
}
offset += parser.getPacketLength();
}
}
}
And here's how you can use it:
class HumidityPacket implements Packet {}
public class Main {
public static void main(String[] args) {
MainParser parser = new MainParser();
//...
parser.registerPacketListener(new PacketListener<HumidityPacket>() {
#Override
public Class<HumidityPacket> getPacketClass() {
return HumidityPacket.class;
}
#Override
public void onPacket(HumidityPacket packet) {
// todo
}
});
}
}
I have error when I send this:
myClaass.put(new ImenResult(1).Show());
myClaass.put(new ImenResult(2));
I understand why I have got this problem because I send to class which have interface parameter. My question is: what I have to add to interface to send this data and haven`t got error because this structure have to be this same (not my decision) because later I can send String or another things (Class). I know this is very difficult question.
Program:
Class_1 myClaass = new Class_1();
myClaass.put(new ImenResult(1).Show());
myClaass.put(new ImenResult(2));
myClaass.put(new ImenResult(3));
ImenResult:
public class ImenResult implements IImenResult{
public Integer i;
public ImenResult(Integer i ) {
this.i = i;
}
#Override
public Integer Show() {
return i;
}
Class_1
public class Class_1 implements IQRack{
public List<IAdant> adan;
public void put(IAdant value) {
adan.add(value);
}
}
IAdan
public interface IAdant {
}
Thanks for help I told you this is difficult question (of course for people who are not Java specialist) but topic is fixed. I gave you points. Thanks again for helped. [closed]
you should implement the method put(Integer) in Class_1 for this to work
public class Class_1 implements IQRack {
public List<IAdant> adan;
public void put(IAdant value) {
adan.add(value);
}
void put(Integer Show) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
UPDATE
After your comment i think this is what you are trying to do:
public class Class_1 implements IQRack {
private List<IAdant> adan = new ArrayList<IAdant>();
public Class_1(IAdant i) {
this.put(i);
}
void put(IAdant Show) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
public class IAdant {
Boolean bValue;
String sValue;
Integer iValue;
public IAdant(Object o) {
if (o.getClass().isInstance(new Integer(1))) {
iValue = (Integer) o;
} else if (o.getClass().isInstance(false)) {
bValue = (Boolean) o;
} else if (o.getClass().isInstance("")) {
sValue = (String) o;
}
}
}
The line: myClaass.put(new ImenResult(1).Show()); will fail, because myClaass.put() is expecting to get an object that implements IAdant, but you are sending and Integer. Integer does not implement IAdant.
myClaass.put(new ImenResult(1).Show());
myClaass.put(new ImenResult(2));
first case would file, cuz, your put method expects IAdant(any that that implements IAdant) and you are passing an integer as the show method would return one.
second case would fail because of the same reason that your ImenResult class is not implementing or a Type of IAdant.
if you want the second case to work, implement IAdant in ImenResult class.
public class ImenResult implements IImenResult,IAdant{
//implement the methods from both the interfaces
}
now myClaass.put(new ImenResult(2)); line would work with out any compiler errors.
If you wanna get the first case myClaass.put(new ImenResult(1).Show()); to compile , you have to overload the put method in myClass class.
public void put(int val) {
//do your logic here
}