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
}
});
}
}
Related
In Java, how can you pass a type as a parameter (or declare as a variable)?
I don't want to pass an instance of the type but the type itself (eg. int, String, etc).
In C#, I can do this:
private void foo(Type t)
{
if (t == typeof(String)) { ... }
else if (t == typeof(int)) { ... }
}
private void bar()
{
foo(typeof(String));
}
Is there a way in Java without passing an instance of type t?
Or do I have to use my own int constants or enum?
Or is there a better way?
Edit: Here is the requirement for foo:
Based on type t, it generates a different short, xml string.
The code in the if/else will be very small (one or two lines) and will use some private class variables.
You could pass a Class<T> in.
private void foo(Class<?> cls) {
if (cls == String.class) { ... }
else if (cls == int.class) { ... }
}
private void bar() {
foo(String.class);
}
Update: the OOP way depends on the functional requirement. Best bet would be an interface defining foo() and two concrete implementations implementing foo() and then just call foo() on the implementation you've at hand. Another way may be a Map<Class<?>, Action> which you could call by actions.get(cls). This is easily to be combined with an interface and concrete implementations: actions.get(cls).foo().
I had a similar question, so I worked up a complete runnable answer below. What I needed to do is pass a class (C) to an object (O) of an unrelated class and have that object (O) emit new objects of class (C) back to me when I asked for them.
The example below shows how this is done. There is a MagicGun class that you load with any subtype of the Projectile class (Pebble, Bullet or NuclearMissle). The interesting is you load it with subtypes of Projectile, but not actual objects of that type. The MagicGun creates the actual object when it's time to shoot.
The Output
You've annoyed the target!
You've holed the target!
You've obliterated the target!
click
click
The Code
import java.util.ArrayList;
import java.util.List;
public class PassAClass {
public static void main(String[] args) {
MagicGun gun = new MagicGun();
gun.loadWith(Pebble.class);
gun.loadWith(Bullet.class);
gun.loadWith(NuclearMissle.class);
//gun.loadWith(Object.class); // Won't compile -- Object is not a Projectile
for(int i=0; i<5; i++){
try {
String effect = gun.shoot().effectOnTarget();
System.out.printf("You've %s the target!\n", effect);
} catch (GunIsEmptyException e) {
System.err.printf("click\n");
}
}
}
}
class MagicGun {
/**
* projectiles holds a list of classes that extend Projectile. Because of erasure, it
* can't hold be a List<? extends Projectile> so we need the SuppressWarning. However
* the only way to add to it is the "loadWith" method which makes it typesafe.
*/
private #SuppressWarnings("rawtypes") List<Class> projectiles = new ArrayList<Class>();
/**
* Load the MagicGun with a new Projectile class.
* #param projectileClass The class of the Projectile to create when it's time to shoot.
*/
public void loadWith(Class<? extends Projectile> projectileClass){
projectiles.add(projectileClass);
}
/**
* Shoot the MagicGun with the next Projectile. Projectiles are shot First In First Out.
* #return A newly created Projectile object.
* #throws GunIsEmptyException
*/
public Projectile shoot() throws GunIsEmptyException{
if (projectiles.isEmpty())
throw new GunIsEmptyException();
Projectile projectile = null;
// We know it must be a Projectile, so the SuppressWarnings is OK
#SuppressWarnings("unchecked") Class<? extends Projectile> projectileClass = projectiles.get(0);
projectiles.remove(0);
try{
// http://www.java2s.com/Code/Java/Language-Basics/ObjectReflectioncreatenewinstance.htm
projectile = projectileClass.newInstance();
} catch (InstantiationException e) {
System.err.println(e);
} catch (IllegalAccessException e) {
System.err.println(e);
}
return projectile;
}
}
abstract class Projectile {
public abstract String effectOnTarget();
}
class Pebble extends Projectile {
#Override public String effectOnTarget() {
return "annoyed";
}
}
class Bullet extends Projectile {
#Override public String effectOnTarget() {
return "holed";
}
}
class NuclearMissle extends Projectile {
#Override public String effectOnTarget() {
return "obliterated";
}
}
class GunIsEmptyException extends Exception {
private static final long serialVersionUID = 4574971294051632635L;
}
Oh, but that's ugly, non-object-oriented code. The moment you see "if/else" and "typeof", you should be thinking polymorphism. This is the wrong way to go. I think generics are your friend here.
How many types do you plan to deal with?
UPDATE:
If you're just talking about String and int, here's one way you might do it. Start with the interface XmlGenerator (enough with "foo"):
package generics;
public interface XmlGenerator<T>
{
String getXml(T value);
}
And the concrete implementation XmlGeneratorImpl:
package generics;
public class XmlGeneratorImpl<T> implements XmlGenerator<T>
{
private Class<T> valueType;
private static final int DEFAULT_CAPACITY = 1024;
public static void main(String [] args)
{
Integer x = 42;
String y = "foobar";
XmlGenerator<Integer> intXmlGenerator = new XmlGeneratorImpl<Integer>(Integer.class);
XmlGenerator<String> stringXmlGenerator = new XmlGeneratorImpl<String>(String.class);
System.out.println("integer: " + intXmlGenerator.getXml(x));
System.out.println("string : " + stringXmlGenerator.getXml(y));
}
public XmlGeneratorImpl(Class<T> clazz)
{
this.valueType = clazz;
}
public String getXml(T value)
{
StringBuilder builder = new StringBuilder(DEFAULT_CAPACITY);
appendTag(builder);
builder.append(value);
appendTag(builder, false);
return builder.toString();
}
private void appendTag(StringBuilder builder) { this.appendTag(builder, false); }
private void appendTag(StringBuilder builder, boolean isClosing)
{
String valueTypeName = valueType.getName();
builder.append("<").append(valueTypeName);
if (isClosing)
{
builder.append("/");
}
builder.append(">");
}
}
If I run this, I get the following result:
integer: <java.lang.Integer>42<java.lang.Integer>
string : <java.lang.String>foobar<java.lang.String>
I don't know if this is what you had in mind.
You should pass a Class...
private void foo(Class<?> t){
if(t == String.class){ ... }
else if(t == int.class){ ... }
}
private void bar()
{
foo(String.class);
}
If you want to pass the type, than the equivalent in Java would be
java.lang.Class
If you want to use a weakly typed method, then you would simply use
java.lang.Object
and the corresponding operator
instanceof
e.g.
private void foo(Object o) {
if(o instanceof String) {
}
}//foo
However, in Java there are primitive types, which are not classes (i.e. int from your example), so you need to be careful.
The real question is what you actually want to achieve here, otherwise it is difficult to answer:
Or is there a better way?
You can pass an instance of java.lang.Class that represents the type, i.e.
private void foo(Class cls)
My Goal: I need to keep the App state exactly in same sate after shutdown, lets say it's equivalent to "suspend" state.
My Problem : I do know that serialization mechanism doesn't save transient variables neither static variables. However I need to maintain the static variables in exactly same state after App suspension/shut down.
Approach-1 : I could save the state of static variable(s) into a different file, using my "file format", and serialize the objects into a different one.
a) Is this the "normal" approach?
Approach-2 : If I extend the ObjectInputStream/ObjectOutputStreamand override the methods readStreamHeader/writeStreamHeaderI can write whatever I want. So I can also write my static variables.
b) Am I doing something I should not?
Here's the code I've written testing approach-2, and seams to work fine. Please note, I'm not a Java programmer, so for it's very important to understand best practices, if there's any in this particular case.
#SuppressWarnings("serial")
class SequenceIdentifier implements Serializable
{
protected static long seqIdentifier_ = 1L; //This variable MUST NOT be reseted.
private long id_; //Object variable to be serialised.
private SequenceIdentifier(long id)
{ id_ = id;
}
#Override
public String toString()
{ return ("Id : " + id_ + " of " + seqIdentifier_);
}
public static SequenceIdentifier newInstance()
{ return new SequenceIdentifier(seqIdentifier_++);
}
}
final class OOStream extends ObjectOutputStream
{
public OOStream(OutputStream out) throws IOException
{ super(out);
}
#Override
protected void writeStreamHeader() throws IOException
{ super.writeLong(SequenceIdentifier.seqIdentifier_);
}
}
final class OIStream extends ObjectInputStream
{
public OIStream(InputStream in) throws IOException
{ super(in);
}
#Override
protected void readStreamHeader() throws IOException
{ SequenceIdentifier.seqIdentifier_ = super.readLong();
}
}
public class Main
{
public static void dump(ArrayList<SequenceIdentifier> ids)
{
for (SequenceIdentifier id : ids)
System.out.println(id);
}
public static void saveData()
{
ArrayList<SequenceIdentifier> ids = new ArrayList<>(Arrays.asList(SequenceIdentifier.newInstance(),
SequenceIdentifier.newInstance(),
SequenceIdentifier.newInstance(),
SequenceIdentifier.newInstance()));
try (OOStream oOut = new OOStream(new FileOutputStream("foo.bin")))
{ oOut.writeObject(ids);
} catch (Exception e)
{ System.err.println(e);
}
dump(ids);
}
#SuppressWarnings("unchecked")
public static void loadData()
{
ArrayList<SequenceIdentifier> ids = null;
try (OIStream oIn = new OIStream(new FileInputStream("foo.bin")))
{ ids = (ArrayList<SequenceIdentifier>)oIn.readObject();
} catch (Exception e)
{ System.err.println(e);
}
dump(ids);
}
public static void main(String[] args)
{
saveData();
System.out.println("Counter at this point " + SequenceIdentifier.seqIdentifier_);
SequenceIdentifier.seqIdentifier_ = 0;
loadData();
System.out.println("Counter at this point " + SequenceIdentifier.seqIdentifier_);
}
}
I would create a separate Memento-class containing all the relevant data as fields and de-/serialize that.
class MyClassWithStaticFields1 {
private static String field;
}
class MyClassWithStaticFields2 {
private static String field;
}
class StaticMemento {
String field1;
String field2;
}
// serialization
StaticMemento mem = new StaticMemento();
mem.field1 = MyClassWithStaticFields1.field;
mem.field2 = MyClassWithStaticFields2.field;
outputStream.writeObject(mem);
// deserialize
StaticMemento mem = outputStream.readObject();
MyClassWithStaticFields1.setField(mem.field1);
MyClassWithStaticFields2.setField(mem.field2);
So basically your Approach-1.
Several possibilities.
Make it non-static.
Write complementary readObect()/writeObject() methods that call defaultReadObject() and defaultWriteObject() respectively and then serialize/deserialize the field.
Write complementary writeReplace()/readResolve() methods that substitute a proxy object that does contain this member as a non-transient non-static member.
Make the object Externalizable and take complete control of the serialization process yourself in the associated methods.
Review your requirement.
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.
I'm writing a messaging system to queue actions for my program to execute. I need to be able to pass various objects by the messages. I currently have a Msg object that accepts (Action enum, Data<?>...object). The Data object is intended to be a wrapper for any object I might pass.
Currently the Data object uses this code, with generics:
public class Data<T> {
private T data;
public Data(T data){
this.data = data;
}
public T getData(){
return data;
}
}
The Msg object takes Data<?>... type, so Msg has a Data<?>[] field.
If getData() is called on a Data<?> object, it returns the Object type. Obviously not ideal.
I need to be able to pass, say, Image objects as well as String objects. I'm certain there's a better way of passing arbitrary data.
The reason you're having trouble is that you're trying to get the static typing system of Java to do something that it can't. Once you convert from a Data<T> to a Data<?>, whatever T was is effectively lost. There's no clean way to get it back.
The quickest way to get it to work (from what you have right now) is to start throwing casts everywhere, like this:
Data<?> d = new Data("Hello");
String contents = (String)d.getData();
This is kind of a terrible idea, so let's go back to the drawing board.
If (ideally), you have all of the types you could ever need ahead of time (i.e. every Data is either a String or an Image or an Integer), then you can pretty easily (though it's a bit tedious) define a Sum type (aka a union if you're coming from C) of the different types of data you'll have to handle. As a class invariant, we assume that exactly one of the fields is non-null, and the rest are null. For this example I'll assume it can be either a String, an Image, or an Integer, but it's fairly simple to add or remove types from Data as necessary.
public class Data {
private Image imgData;
private String stringData;
private Integer intData;
public Data(Image img) {
this.imgData = img;
}
public Data(String stringData) {
this.stringData = stringData;
}
public Data(Integer intData) {
this.intData = intData;
}
public boolean isImage() {
return imageData != null;
}
public boolean isInteger() {
return intData != null;
}
public boolean isString() {
return stringData != null;
}
public Image asImage() {
if(! isImage()) throw new RuntimeException();
return imgData;
}
public Image asString() {
if(! isString()) throw new RuntimeException();
return stringData;
}
public Image asInt() {
if(! isInt()) throw new RuntimeException();
return intData;
}
}
One necessary side effect is that we cannot wrap null without causing exceptional behavior. Is this is desired, it isn't too difficult to modify the class to allow for it.
With this Data class, it's pretty easy to do if-else logic to parse it.
Data d = ....... //Get a data from somewhere
if(d.isImage()) {
Image img = d.asImage();
//...
} else if (d.isString()) {
String string = d.asString();
//...
} else if (d.isInteger()) {
Integer i = d.asInt();
//...
} else {
throw new RuntimeException("Illegal data " + d + " received");
}
If you call getData().getClass() you will get the class or type that was passed, which doesn't seem to me to be the same as an Object. You might not know what you are getting, but you can either find out or define a common interface for everything you might pass. You could for example, call toString() or getClass() on anything passed. Your question is that you are passing any conceivable object, so my question is what are you going to do with it? If you are going to serialize it into a database you don't need know anything about what type it is, otherwise you can test it or call a common interface.
public class PlayData {
class Msg {
private List<Data<?>> message = new ArrayList<Data<?>>();
public void addData(Data<?> datum) { message.add(datum); }
public void printTypes() { for ( Data<?> datum: message ) { System.out.println(datum.getData().getClass()); } }
}
class Data<T> {
private T value;
public Data(T value) { this.value = value; }
public T getData() { return value; }
}
class Listener {
public void receive(Msg msg) { msg.printTypes(); }
}
class Sender {
private Listener listener;
public Sender(Listener listener) { this.listener = listener; }
public void send(Msg msg) { listener.receive(msg); }
}
class MyPacket {
int i;
public MyPacket(int i) { this.i = i; }
}
public static void main(String[] args) throws Exception { new PlayData().run(); }
public void run() throws Exception {
Sender sender = new Sender(new Listener());
Msg msg = new Msg();
msg.addData(new Data<String>("testing") );
msg.addData(new Data<MyPacket>(new MyPacket(42)) );
sender.send(msg);
}
}
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