Java interface design - java

I had an interface initially as below.
public interface testMe {
public Set<String> doSomething();
}
public class A implements testMe {
public Set<String> doSomething() {
return // Set<String>
}
}
I had similar classes implementing testMe. Now I have to add one more class which returns Set<Some Object>
public class X implements testMe() {
public Set<Some OBject> doSomething() {
}
}
How could i add this method in the interface without breaking existing classes?

You can use
public interface testMe {
public Set<?> doSomething();
}
Or
public interface testMe {
public Set<? extends CommonSuperclass> doSomething();
}

You can't for two reasons.
A class or interface can't have two or more methods that have the same number and type of parameters with the same name but differing return types; and
Because of type erasure, all Set<...> instances are, at runtime, simply Set, so they would have the exact same return type anyway.
You will need to name the second something different.
The more complicated answer is that you can make the parameter type extensible:
public interface TestMe<T extends Serializable> {
Set<T> doSomething();
}
public class A implements TestMe<String> {
#Override
public Set<String> doSomething() { ... }
}
public class X implements TestMe<ASerializableObject> {
#Override
public Set<ASerializableObject> doSomething() { ... }
}

I don't believe you can, because type erasure will ruin the effect you have in mind.
You can parameterize the interface:
import java.util.Set;
public interface ISomething<T>
{
Set<T> doSomething(T [] data);
}
And the implementation:
import java.util.HashSet;
import java.util.Set;
public class Something<T> implements ISomething<T>
{
public static void main(String[] args)
{
Something<String> something = new Something<String>();
Set<String> set = something.doSomething(args);
System.out.println(set);
}
public Set<T> doSomething(T [] data)
{
Set<T> foo = new HashSet<T>();
for (T x : data)
{
foo.add(x);
}
return foo;
}
}
I'm not sure this accomplishes what you have in mind, though.

Related

Abstract method with different parameters Java

public abstract class CommonClass {
abstract void send(<what should i put here???>) {}
}
public class ClassA extends CommonClass {
void send(List<Comments> commentsList) {
// do stuff
}
}
public class ClassB extends CommonClass {
void send(List<Post> postList) {
// do stuff
}
}
I am new to OODP, I am trying to have a method that is able to take in any kind of List data so that I can abstract things out. How can i do this?
You could make it generic on some type T. Like,
public abstract class CommonClass<T> {
abstract void send(List<T> al);
}
And then, to implement it - use the generic. Like,
public class ClassA extends CommonClass<Comments> {
#Override
void send(List<Comments> commentsList) {
// do stuff
}
}
public class ClassB extends CommonClass<Post> {
#Override
void send(List<Post> postList) {
// do stuff
}
}
Also, as discussed in the comments, your class names could be improved to be more intuitive; something like,
public abstract class AbstractSender<T> {
abstract void send(List<T> al);
}
and then
public class CommentSender extends AbstractSender<Comment> {
#Override
void send(List<Comment> commentsList) {
// do stuff
}
}
public class PostSender extends AbstractSender<Post> {
#Override
void send(List<Post> postList) {
// do stuff
}
}
That has the advantage(s) of being more readable and easier to reason about (I can tell what a PostSender does by reading the name, ClassB not so much).
Finally, this looks like a case where an interface would work since your abstract class is purely virtual (and should be preferred since you can implement multiple interface, but can only extend from a single parent class);
public interface ISender<T> {
void send(List<T> al);
}
public class CommentSender implements ISender<Comment> {
#Override
void send(List<Comment> commentsList) {
// do stuff
}
}
public class PostSender implements ISender<Post> {
#Override
void send(List<Post> postList) {
// do stuff
}
}
In order to achieve this, you can take multiple approaches, I would suggest looking into Generics: https://docs.oracle.com/javase/tutorial/java/generics/index.html
With that said, there is one approach that is the most elegant and simple: you can supply a List<T> where T is a generic type.
public abstract class CommonClass<T> {
abstract void send(List<T>) {}
}
public class ClassA extends CommonClass<Comment> {
void send(List<Comments> commentsList) {
// do stuff
}
}
public class ClassB extends CommonClass<Post> {
void send(List<Post> postList) {
// do stuff
}
}
You can do that with the help of generics. https://www.tutorialspoint.com/java/java_generics.htm
Example
The abstract class
public abstract class CommonClass {
public abstract <T> void send(List<T> data);
}
Its child
public class Child extends CommonClass {
public <T> void send(List<T> data) {
// code here
}
}
Retrieving the list's contents
Retrieving the generified list's contents is similar to retrieving any list's contents. In the scope of the method, "T" is a type of object contained in the list.
for (T t : data) {
// to check if t is a string
if (t instanceof String) {
// code
}
}
You can also use lambdas to retrieve every element in the list.

How to deliver the class of a generic type to a method in Java?

I want to implement a class that instantiates generic types.
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public void doSomething(E Element) {
T set = setClass.newInstance();
set.add(element);
}
}
I tried instantiating the class like this:
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet<>(HashSet<Integer>.class);
However using .class on a generic type does not seem to be allowed. How would I correctly pass the required Class of a generic type to the constructor?
Not sure it is good to expose the inner set type (Hash versus other) in the parameterized type.
Actually due to type erasure you can't instantiate parameterised types directly, but you can pass in a factory,
package langGenerics;
import java.util.HashSet;
import java.util.Set;
public class UseGenerics {
public static void main(String[] args) {
SetFactory<Integer> setFactory = HashSet::new;
DisjointSet<Integer> disjointSet = new DisjointSet<>(setFactory);
disjointSet.doSomething( 123 );
}
}
interface SetFactory<T> { Set<T> get(); }
class DisjointSet<T> {
private SetFactory<T> setFactory;
public DisjointSet(SetFactory<T> setFactory) {
this.setFactory = setFactory;
}
public void doSomething(T item) {
Set<T> set = setFactory.get();
set.add(item);
}
}
If you really want to init your own set storage, then I suggest you to pass Supplier to your constructor:
public static class DisjointSet<T extends Set<E>, E> {
T set;
public DisjointSet(Supplier<T> supplier) {
set = supplier.get();
}
public void doSomething(E element) {
set.add(element);
}
}
Then use it:
DisjointSet<HashSet<Integer>, Integer> set = new DisjointSet<>(HashSet::new);
if this is what you wanted,
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public static void main(String[] args) {
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet(new HashSet<Integer>().getClass());
}
}

Avoid cast in a generics hierarchy

I have some difficulty to simplify more the problem. Sorry if they are too many code here.
I try to improve the architecture of the code above because I hate warning and cast and I feel something wrong.
Now, the code.
I have a util class with these two parametrized methods (same signature as OpenJPA's CriteriaBuilder...)
public class MyUtil {
public void equal(List<?> l, Object value) {
// do something (see CriteriaBuilder.equal method)
}
public <Y extends Comparable<? super Y>> void greaterThan(List<? extends Y> l, Y value) {
// do something (see CriteriaBuilder.greaterThan method)
}
}
Then, I want to be able to abstract them to call it via an interface.
public interface IOperation<T> {
// maybe make this method generic ? but how ?
public abstract void doOp(List<T> l, T value);
}
public abstract class AbstractOperation<T> implements IOperation<T> {
protected MyUtil myUtil;
}
public class EqualOp extends AbstractOperation<Object> {
#Override
public void doOp(List<Object> path, Object value) {
myUtil.equal(path, value);
}
}
public class GreaterThanOp<T extends Comparable<? super T>> extends AbstractOperation<T> {
#Override
public void doOp(List<T> path, T value) {
myUtil.greaterThan(path, value);
}
}
I create a factory
public class OperationFactory {
private static OperationFactory instance;
public static OperationFactory getInstance() {...}
public IOperation<?> get(String op) {
if ("=".equals(op)) {
return new EqualOp();
} else if (">".equals(op)) {
return new GreaterThanOp<Comparable<? super Object>>();
}
throw new InvalidParameterException();
}
}
Then I use it :
public class Client {
public void needOp(String op) {
IOperation<String> operation = (IOperation<String>) OperationFactory.getInstance().get(op); // How to avoid this cast ?
List<String> l = null;
operation.doOp(l, "a string");
}
}
My question is : is it possible to avoid this cast in the Client class ? How ? Is there a way to have a better architecture ?
Thanks for reading
I'm assuming you can require your type to be Comparable.
Parameterize EqualOp like GreaterThanOp:
public class EqualOp<T extends Comparable<T>> extends AbstractOperation<T> {
#Override public void doOp(List<T> path, T value) ...
And define get() like this:
public <T extends Comparable<T>> IOperation<T> get(String op) {
if ("=".equals(op)) {
return new EqualOp<T>();
} else if (">".equals(op)) {
return new GreaterThanOp<T>();
}
...

Java generics passing parameters

Hope somebody can help me out of this confussion.
I made this method:
public static <T> void myMethod(Map<Class<T>, MyInterface<T>> map) {
}
Used paramter T in order to make sure that the class used as key is the same as the class used as parameter in MyInterface.
Now I want to pass a map which different classes as keys, of course, and corresponding implementations of MyInterface.
But it doesn't work, getting syntax errors because of type parameters. Here is the code, I hope is self explanatory.
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<Class<?>, MyInterface<?>> map = new HashMap<Class<?>, MyInterface<?>>();
// Map<Class<Object>, MyInterface<Object>> map = new HashMap<Class<Object>, MyInterface<Object>>();
map.put(Object.class, new MyObjectImpl());
//if I use Map<Class<Object>, MyInterface<Object>> I get a compiler error here
//because map<String> is not map<Object> basically
map.put(String.class, new MyStringImpl());
//this would be possible using <?>, which is exactly what I don't want
// map.put(String.class, new MyIntegerImpl());
//<?> generates anyways a compiler error
myMethod(map);
}
//use T to make sure the class used as key is the same as the class of the parameter "object" in doSomething
public static <T> void myMethod(Map<Class<T>, MyInterface<T>> map) {
}
interface MyInterface<T> {
void doSomething(T object);
}
static class MyObjectImpl implements MyInterface<Object> {
#Override
public void doSomething(Object object) {
System.out.println("MyObjectImpl doSomething");
}
}
static class MyStringImpl implements MyInterface<String> {
#Override
public void doSomething(String object) {
System.out.println("MyStringImpl doSomething");
}
}
static class MyIntegerImpl implements MyInterface<Integer> {
#Override
public void doSomething(Integer object) {
System.out.println("MyIntegerImpl doSomething");
}
}
}
You can't do that, because there is no constraint defined in Map's put() method between the key and the value. If you want to assure that your map is populated properly (i.e. create such constraint), hide the map behind some API that will check the correctness, for example:
public <T> void registerInterface(Class<T> clazz, MyInterface<T> intf) {
map.put(clazz, intf);
}
Then, just call the registerInterface instead of manually populating the map.
As far as I know, you cannot declare a Map like you describe in Java. All you can do is performing type checking and/or add constraints.
Guava offers something that approaches your problem with ClassToInstanceMap. So one way to do this would be to use MapConstraints.constrainedMap (like the example below)
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.MapConstraint;
import com.google.common.collect.MapConstraints;
public class Main {
interface MyInterface<T> {
void doSomething(T object);
Class<T> getType();
}
static class MyObjectImpl implements MyInterface<Object> {
#Override
public void doSomething(Object object) {
System.out.println("MyObjectImpl doSomething");
}
#Override
public Class<Object> getType() {
return Object.class;
}
}
static class MyStringImpl implements MyInterface<String> {
#Override
public void doSomething(String object) {
System.out.println("MyStringImpl doSomething");
}
#Override
public Class<String> getType() {
return String.class;
}
}
static class MyIntegerImpl implements MyInterface<Integer> {
#Override
public void doSomething(Integer object) {
System.out.println("MyIntegerImpl doSomething");
}
#Override
public Class<Integer> getType() {
return Integer.class;
}
}
public static void main(String[] args) throws ParseException {
Map<Class<?>, MyInterface<?>> map = MapConstraints.constrainedMap(new HashMap<Class<?>, Main.MyInterface<?>>(),
new MapConstraint<Class<?>, MyInterface<?>>() {
#Override
public void checkKeyValue(Class<?> key, MyInterface<?> value) {
if (value == null) {
throw new NullPointerException("value cannot be null");
}
if (value.getType() != key) {
throw new IllegalArgumentException("Value is not of the correct type");
}
}
});
map.put(Integer.class, new MyIntegerImpl());
map.put(String.class, new MyStringImpl());
map.put(Object.class, new MyObjectImpl());
map.put(Float.class, new MyIntegerImpl()); //<-- Here you will get an exception
}
}
I do not think this is possible :
Class<T> only ever accepts T.class as value. Class<Object> will not accept String.class, even though Object is a superclass of String.
For this reason any map with Class<T> as key can have only one element, with T.class as key value, whatever the value of T.
The compiler will only ever accept a map with a definite value of T as parameter. You cannot write Map<Class<?>, MyInterface<?>> because each ? is assumed to be different : it does not match Map<Class<T>, MyInterface<T>> which requires T to have the same value.
That said, myMethod will only ever accept single-entry maps, which does not seem useful.
Change your method signature to
public static <T> void myMethod(Map<Class<? extends T>, MyInterface<? extends T>> map) {
}
now your declaration and invocation should work..
Map<Class<?>, MyInterface<?>> map = new HashMap<Class<?>, MyInterface<?>>();
map.put(Integer.class, new MyIntegerImpl());
map.put(String.class, new MyStringImpl());
map.put(Object.class, new MyObjectImpl());
myMethod(map);

Java generics wildcards

public interface UnivariateOperator<T> {
public TimeSeries<T> operateOn(TimeSeries<T> timeseries);
}
public class SamplingOperator<T> implements UnivariateOperator<T> {
#Override
public TimeSeries<T> sample(TimeSeries<T> timeseries) {
...
}
}
Is there a way to use wildcards so the sampling operator can work with any type? I don't really want to have to specify the type for the sampling operator...it should work with any typed timeseries.
What if you did something like this:
public class SamplingOperator<T> implements UnivariateOperator<T> {
private SamplingOperator(){
}
#Override
public TimeSeries<T> sample(TimeSeries<T> timeseries) {
...
}
public static SamplingOperator<? extends Object> getInstance() {
return new SamplingOperator<Object>();
}
}
This ensures that any instance of SamplingOperator will be able to accept any type of TimeSeries as an argument to its sample method.
There are probably better solutions out there, but this one will work.
You can't, because you need to specify the generic of UnivariateOperator. If you just want a generic method that samples TimeSeries, you will need something like
public class TimeSeriesSampler {
public static <T> TimeSeries<T> sample(TimeSeries<T> timeseries) {
...
}
}
but if you want a SamplingOperator to implements UnivariantOperator, you will need to specify the generic. If you still don't want to specify, you could use something as
public class SamplingOperator implements UnivariateOperatior<Object> {
private SamplingOperator(){
}
public <T> TimeSeries<T> sample(TimeSeries<T> timeseries) {
return null;
}
#Override
public TimeSeries<Object> operateOn(TimeSeries<Object> timeseries) {
...
}
}
but you will lose the power of the generic. Another way is
public class SamplingOperator<S> implements UnivariateOperatior<S> {
private SamplingOperator(){
}
public <T> TimeSeries<T> sample(TimeSeries<T> timeseries) {
return null;
}
#Override
public TimeSeries<S> operateOn(TimeSeries<S> timeseries) {
return timeseries;
}
}
but it "smells" bad, as the sample method gives a feeling of a class method, instead of an instance one. It's your choice what's bst to do.
implements UnivariateOperator<Object>

Categories

Resources