Tagging interfaces

V

VisionSet

Why exactly are they bad?

I have a entity, a collection of which is to be associated with an object
that has no need to call any methods on or know anymore than the entity is
of a certain type. These entities are then passed back to the object that
instantiated them which can cast to the concrete type to do something useful
with them. So a tagging interface seems to fit the bill for my entity.
 
J

Jeffrey Schwab

VisionSet said:
Why exactly are they bad?

I have a entity, a collection of which is to be associated with an object
that has no need to call any methods on or know anymore than the entity is
of a certain type. These entities are then passed back to the object that
instantiated them which can cast to the concrete type to do something useful
with them. So a tagging interface seems to fit the bill for my entity.

Please correct me if I have misunderstood anything:

Each "Entity" requires type-specific processing, but you want the code
for all the processing, for all the different types, to be defined in
one "Master" object.

The Master, which actually created all of these Entities, forgets the
Entities' exact types at some point. It therefore wants them to be
"tagged," possibly by having them implement empty but nominally
different interfaces.

This sounds like a perfect case for polymorphism. Forget all the
tagging interfaces. Have all the Entities implement just one interface,
and each concrete Entity type can implement the interface differently.
If you really want to keep the processing code in the Master object, let
each concrete Entity type just immediately invoke a type-specific
callback method in the Master.

If this isn't clear, or doesn't sound like a good idea, let me know &
I'll whip up some example code.
 
V

VisionSet

Jeffrey Schwab said:
Please correct me if I have misunderstood anything:

Each "Entity" requires type-specific processing, but you want the code
for all the processing, for all the different types, to be defined in
one "Master" object.

The number of types (tagged types) and number of master objects is a 1:1
relationship
In fact there is only ever likely to be one entity type and one master
object, but that is not important.
We shall assume other types may exist at some point.
The Master, which actually created all of these Entities, forgets the
Entities' exact types at some point. It therefore wants them to be
"tagged," possibly by having them implement empty but nominally
different interfaces.

The tagging is merely type safety mechanism so the surrogate collector can
only collect a certain type, and the master only gets back what it expects.
 
J

Jeffrey Schwab

VisionSet said:
The number of types (tagged types) and number of master objects is a 1:1
relationship
In fact there is only ever likely to be one entity type and one master
object, but that is not important.
We shall assume other types may exist at some point.




The tagging is merely type safety mechanism so the surrogate collector can
only collect a certain type, and the master only gets back what it expects.

Wouldn't it be easier, and make your intent clearer, to use generics?
 
J

Jeffrey Schwab

VisionSet said:
I'll still need a type, no?

Collection<? extends TagIntf>

You said you wanted the surrogate collector to "only collect a certain
type." You should be able to use this type as the parameter for the
generic container, e.g. Collection<Entity>. No?
 
V

VisionSet

You said you wanted the surrogate collector to "only collect a certain
type." You should be able to use this type as the parameter for the
generic container, e.g. Collection<Entity>. No?

I said I wanted to assume that the 1 type may grow to include others,
although only 1 type would be used in any 1 instance of the program, but
that is irrelevent I want the code to be able to support that eventuallity.
Hence the tagging interface.

But I'd heard there really was no defence for tagging interface approach,
and that is is merely a hack to allow introspection. But when you are faced
with the situation where an objects references are collected at various
places because that is where they naturally belong, and that is the best way
to manage there existence, but no methods are called on them, and you want
to decouple their logic, then this seems like an okay use to me.
 
A

Andrew McDonagh

VisionSet said:
Why exactly are they bad?

I have a entity, a collection of which is to be associated with an object
that has no need to call any methods on or know anymore than the entity is
of a certain type. These entities are then passed back to the object that
instantiated them which can cast to the concrete type to do something useful
with them. So a tagging interface seems to fit the bill for my entity.

Before 1.5, Tagging (or more commonly know as 'Marker') Interfaces were
a good way of achieving the same effect as 1.5 Annotations.

They were never bad - mis-understood and therefore mis-used.

Now we have annotations there is certainly less need for them.

Andrew
 
C

Chris Smith

VisionSet said:
Why exactly are they bad?

My thoughts on the matter.

If you think of what you're doing as a tag interface, then it's probably
a bad idea, at least as of Java 1.5. The reason is that interfaces are
part of the type system, and as such they imply a certain relationship
between the types of objects that implement them.

There are two ways you can solve this apparent mismatch.

(a) Give up on making the "tag" a type. With Java 1.5, you'd then
decide to use an annotation, instead. Annotations don't define type
system relationships, but they do allow you to note information about a
type that can be used (among other places) dynamically at runtime. For
example, if Java were newly designed today, Cloneable and Serializable
would probably be annotations, because no one ever keeps a variable of
type Serializable or Cloneable.

(b) The other direction: stop thinking of it as a tagging interface, and
start thinking of it as a full-fledged type. Even if that type doesn't
necessarily need to declare any methods at all (as in your example), it
is a type... and it COULD declare methods. Once you're comfortable with
that, stop calling it a tagging interface, and stop worrying about bad
design. (Of course, if you then think of operations that make sense and
are useful to your application on all referents of that type, by all
means add them to the interface... especially if this allows you to take
advantage of polymorphism.)

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
J

John C. Bollinger

Chris said:
if Java were newly designed today, Cloneable and Serializable
would probably be annotations, because no one ever keeps a variable of
type Serializable or Cloneable.

I do. I at times keep variables and declare method parameters and
return types as Serializable in classes that implement Serializable, so
as to improve the likelihood that instances can in fact successfully be
serialized. Naturally, this tends to involve general-purpose classes
where the declared types would otherwise be Object. I haven't done this
with Java 1.5 yet, but in a generic class or method I might use
Serializable as an upper type bound or (intentionally) in an
intersection type. It does not seem that this paradigm ever caught on
in the wider world, but it does lead me to accept Serializable as a very
reasonable type.

In the context of the larger discussion of tag interfaces, however, the
reason that Serializable as an interface makes any sense is that it has
special meaning to the VM. A type should convey useful information
about objects, and for interfaces without special meaning to the VM, it
is my opinion that at least one method is necessary to make the
information conveyed useful.
 
C

Chris Smith

John C. Bollinger said:
In the context of the larger discussion of tag interfaces, however, the
reason that Serializable as an interface makes any sense is that it has
special meaning to the VM. A type should convey useful information
about objects, and for interfaces without special meaning to the VM, it
is my opinion that at least one method is necessary to make the
information conveyed useful.

I'm not clear on the details of Mike's case... but if he's being
generally accurate in his description, Mike may have a counter-example
to your statement.

In his case, Mike is apparently keeping objects in some kind of
specialized data structure whose behavior (presumably) only makes sense
for objects of that specific type. The type may not have any general
new operations to introduce, but in may still imply certain constraints
in the way that its objects should be managed. It may even further
constrain the implementations of inherited method declarations, such as
Object's equals and hashCode. Much like in the Collections API, then,
the client code is arranged to know the type when it retrieves the
object.

Of course, this new data structure type *could* be written with a type
of Object, but that would imply that it works properly and makes sense
for all Object references... something that may not actually be the
case. I would think this is exactly the right use for a type, new
operations or not.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

Chris Smith

John C. Bollinger said:
I do. I at times keep variables and declare method parameters and
return types as Serializable in classes that implement Serializable, so
as to improve the likelihood that instances can in fact successfully be
serialized.

Perhaps. I tend to see this as too leaky to be worthwhile. You're
impacting a lot of code in order to build something that doesn't provide
any additional guarantees. After all, ArrayList implements Serializable
yet can't guarantee that it can be serialized.

Worse, though, you impact client code here as well. There are plenty of
good reasons to hold references to objects that you know are
serializable, and yet not use a serializable type. For example, I often
keep references of type List for obvious reasons, yet List does not
extend Serializable. I may need to add casts to my code in order to use
an API that uses Serializable as a type, and yet there's no good way
that I could modify my code to hold variables of type Serializable in
the first place.

In the end, I dismiss use of Serializable as a type as being without
significant merit. If it could make serialization safe, of course, that
would be phenomenal... but it can't. Instead, it only adds more static
hurdles to using what is ultimately a runtime-checked feature anyway.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
V

VisionSet

I'm not clear on the details of Mike's case... but if he's being
generally accurate in his description, Mike may have a counter-example
to your statement.

Okay, since I've generated some interest, I'll expand.

I'm attempting to decouple the method of networking from the work that the
server does.
I have a 2 way socket based network layer that can initiate communication,
handle requests and reply with responses in either direction.
This Class is passed handler objects which deal with the requests and return
something appropriate.
In order for the server to pass the correct stuff to specific interested
clients the business part of the server must keep references to its clients.
These references must know how to locate the client but the business part of
the server does not need to know this. So ClientRef has some business stuff
and also a Location reference. It is this location reference that can be an
empty interface type. The business layer never needs to know anything other
than it is some form of locator. The network layer created the Location and
when it gets it back will know how to interrogate it for the address.
I suppose this is by the by, but I guess port and ip address is only ever
going to be the information you need to locate. But I don't know that for
sure, so the principle stands.
 
J

John C. Bollinger

Chris said:
Perhaps. I tend to see this as too leaky to be worthwhile. You're
impacting a lot of code in order to build something that doesn't provide
any additional guarantees. After all, ArrayList implements Serializable
yet can't guarantee that it can be serialized.

I did say I wanted to "improve the likelihood" of being able to
serialize instances, not that I wanted to ensure successful
serialization. If the approach I describe were ubiquitous then it would
be much more effective.
Worse, though, you impact client code here as well. There are plenty of
good reasons to hold references to objects that you know are
serializable, and yet not use a serializable type. For example, I often
keep references of type List for obvious reasons, yet List does not
extend Serializable. I may need to add casts to my code in order to use
an API that uses Serializable as a type, and yet there's no good way
that I could modify my code to hold variables of type Serializable in
the first place.

I disagree. For a class whose instances are expected to routinely be
serialized, declaring method parameters to be Serializable both
reinforces the point with the class' users and provides some degree of
compile-time checking. It is incorrect to keep references of a
non-Serializable type and nevertheless expect the referenced objects to
be Serializable, even if you "know" that they really are, just as it is
incorrect to keep references of type Collection and yet assume that the
referenced objects are Lists. Indeed, the error is precisely in relying
on knowledge about the objects' classes that is not reflected in their
references' declared types. Typecasts (of reference types) are usually
a sign of this kind of error, with the only exception I can think of
being casting to disambiguate an overloaded method invocation.

By the way, the type safe solution in the List example is not to cast,
but rather "new ArrayList<SomeSerializableType>(myAnyList)".

(Aside: avoiding most need to engage in the kind of incorrect coding
discussed above is by far my favorite aspect of both generics and
covariant return types.)
In the end, I dismiss use of Serializable as a type as being without
significant merit. If it could make serialization safe, of course, that
would be phenomenal... but it can't. Instead, it only adds more static
hurdles to using what is ultimately a runtime-checked feature anyway.

No, it can't make serialization safe, because serialization is
fundamentally unsafe. I remain convinced, however, that it makes
serialization saf*er* -- by making some of the class' expectations
explicit in its structure -- and, moreover, that it presents no
additional hurdle whatsoever to otherwise typesafe code.
 
J

John C. Bollinger

Chris said:
I'm not clear on the details of Mike's case... but if he's being
generally accurate in his description, Mike may have a counter-example
to your statement.

In his case, Mike is apparently keeping objects in some kind of
specialized data structure whose behavior (presumably) only makes sense
for objects of that specific type. The type may not have any general
new operations to introduce, but in may still imply certain constraints
in the way that its objects should be managed. It may even further
constrain the implementations of inherited method declarations, such as
Object's equals and hashCode. Much like in the Collections API, then,
the client code is arranged to know the type when it retrieves the
object.

Of course, this new data structure type *could* be written with a type
of Object, but that would imply that it works properly and makes sense
for all Object references... something that may not actually be the
case. I would think this is exactly the right use for a type, new
operations or not.

I'm still not entirely clear on the details of Mike's case even after he
expanded on it, but I am so far not swayed from my position. My
argument on this point is much the same as on the Serializable point: it
is incorrect to *do* anything with a reference of a type that has no
members, other than simply to hold it and pass it around, because you
must then make some assumption about the object that is not ensured by
its type. On the other hand, using a tag interface as the type is just
as bad as using Object: it implies that any object of the tag interface
type is appropriate for use with the application, which is unlikely to
be true. (Try passing it a "new YourTagInterface() { void darnMySocks()
{}}".) Somewhere, some time, some body is going to have to cast that
reference to make use of the object, and therein is the indicator of a
problem.

(Of course, if the tag interface extends Serializable, then you can at
least expect to be able to serialize instances.... ;-))
 
C

Chris Smith

John C. Bollinger said:
I disagree. For a class whose instances are expected to routinely be
serialized, declaring method parameters to be Serializable both
reinforces the point with the class' users and provides some degree of
compile-time checking. It is incorrect to keep references of a
non-Serializable type and nevertheless expect the referenced objects to
be Serializable, even if you "know" that they really are, just as it is
incorrect to keep references of type Collection and yet assume that the
referenced objects are Lists.

Even accepting this idea, you are forced to make a choice eventually
between several good design ideas. Perhaps it would be good to extend
Java to allow type (List & Serializable), in the manner of generic type
bounds... but that feature isn't currently available. I'd certainly far
rather use List and imply Serializable than use ArrayList just to get
the Serializable statically declared.

I just don't see that there's much benefit to half-solving problems of
verifiability. It's still not verifiable, and that's the end of the
story.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

Chris Smith

John C. Bollinger said:
I'm still not entirely clear on the details of Mike's case even after he
expanded on it, but I am so far not swayed from my position.

I'm also not swayed, now that I've read Mike's description... but I
think I'm unswayed in the opposite direction as you (if I understand you
correctly). In this case, I actually think that Mike should be using
Object as the type; no interface, no annotation. The reason is that a
"location" may be of pretty much any type, not just one that was
designed to work with this data structure. The only requirement is that
it is later interpretable by the same piece of code that created it.

I don't share the same hostility you express toward casts. When they
are checked at runtime as Java's casts are, casting is just a perfectly
normal way of telling the compiler some additional information about a
type. It need not be seen as terribly nefarious. In Mike's case as
described, using an interface type doesn't just mean modifying classes;
it means potentially creating a new class when InetSocketAddress may be
perfectly good enough!

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
J

John C. Bollinger

Chris said:
Perhaps it would be good to extend
Java to allow type (List & Serializable), in the manner of generic type
bounds... but that feature isn't currently available. I'd certainly far
rather use List and imply Serializable than use ArrayList just to get
the Serializable statically declared.

Believe it or not, I almost wrote something to that effect myself. I
would be downright *delighted* to be able to use intersection types in
declarations.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top