In a real use case there were two different types of sensors that had
a common interface/info to be analyzed. It took a bit to figure out how to
deal with this. It certainly wasn't convenient.
Imagine the tuple type hierarchy:
class Base {};
class Derived1 extends Base {};
TStream<Derived1> sDerived1 = ...; // ingest
class Derived2 extends Base {};
TStream<Derived2> sDerived2 = ...; // ingest
TStream<Base> sBase = sDerived1.union( sDerived2 );
doAnalytics( sBase );
The union() gets a compilation error because TStream<Derived*> doesn't extend
TStream<Base>. Std java generics stuff. A straightforward cast doesn't work;
it requires a bit more convoluted casting.
We could make this much easier for users if we added:
TStream<T>:
// typesafe cast a TStream<T> to a TStream<U>
// compile error if U doesn't extend T
<U extends T> TStream<U> cast(Class<U> classU);
Then one could write:
TStream<Base> sBase = sDerived1.cast(Base.class).union(
sDerived2.cast(Base.class) );
Does this seem like the right thing to do? Alternatives?
Fwiw, the implementation of cast() is trivial once you know what to do:
<U extends T> TStream<U> cast(Class<U> classU) {
TStream<?> s = (TStream<?>) this;
@SuppressWarnings("unchecked")
TStream<U> u = (TStream<U>) s;
return u;
}
Having the cast() as a TStream method makes its use much more convenient as
compared to having cast() as some static utility method.
I didn't have a need for this but similarly we could add:
TStream<T>:
// semi-typesafe downcast a TStream<T> to a TStream<U>
// compile error if U doesn't extend T
// runtime error if any particular tuple from the TStream<T> isn't a U
<U extends T> TStream<U> downcast(Class<U> classU);
-- Dale [email protected]