I was trying to understand how to avoid double entry in a clean and "designable" way to the following scenario:
public interface ICommandAble
{ }
public interface ILogAble extends ICommandAble
{ }
public interface IMonitorAble extends ICommandAble
{ }
public abstract class ClassAbs
{ }
public class A extends ClassAbs implements IMonitorAble, ILogAble
{ }
Test method:
public void test()
{
A a=new A();
List<ICommandAble>commandList=new ArrayList<ICommandAble>()
if (a instanceof ILogAble)
{
ILogAble logAbleItem=(ILogAble)a;
commandList.add(logAbleItem);
}
if (a instanceof IMonitorAble) {
IMonitorAble monitorAbleItem=(IMonitorAble)a;
commandList.add(monitorAbleItem);
}
for(ICommandAble item: commandList)
{
if(item instanceof IMonitorAble)
{
log.debug("is monitorable");
}
if(item instanceof ILogAble)
{
log.debug("is logable");
}
}
the output is:
2013-04-22 18:25:00,498 com... [DEBUG] is monitorable
2013-04-22 18:25:02,150 com.... [DEBUG] is logable
2013-04-22 18:25:08,322 com.... [DEBUG] is monitorable
2013-04-22 18:25:08,977 com.... [DEBUG] is logable
That will cause my program to do double executions for each "xAble"
I expected to see:
2013-04-22 18:25:00,498 com... [DEBUG] is monitorable
2013-04-22 18:25:02,150 com.... [DEBUG] is logable
How should I redesign this(in a clean way) that the second iterator conditionion will be based on the reference type and not on the instance type. getting something like this:
2013-04-22 18:25:00,498 com... [DEBUG] is monitorable
2013-04-22 18:25:02,150 com.... [DEBUG] is logable
Think that in the future I might have additional "ables"
Thanks,
ray.
If you are testing objects to see if they are instances of interfaces then they must implement that interface. They can always implement other interfaces too. In your scenario, to achieve the output you need you must make the objects only implement one of the two interfaces.
public class Test {
public interface ICommandAble {
}
public interface ILogAble extends ICommandAble {
}
public interface IMonitorAble extends ICommandAble {
}
public abstract class ClassAbs {
}
public class A extends ClassAbs implements IMonitorAble, ILogAble {
}
public class B extends ClassAbs implements IMonitorAble {
}
public class C extends ClassAbs implements ILogAble {
}
public void test() {
A a = new A();
B b = new B();
C c = new C();
List<ICommandAble> commandList = new ArrayList<ICommandAble>();
commandList.add(a); // Remove this line to just get the two printouts.
commandList.add(b);
commandList.add(c);
for (ICommandAble item : commandList) {
if (item instanceof IMonitorAble) {
System.out.println(item.getClass().getSimpleName() + " is monitorable");
}
if (item instanceof ILogAble) {
System.out.println(item.getClass().getSimpleName() + " is logable");
}
}
}
public static void main(String args[]) {
new Test().test();
}
}
prints
A is monitorable
A is logable
B is monitorable
C is logable
I have added an A to the list too to demonstrate the difference.
As you are only adding items to an List, you can change your code to verify if it is an instance of ICommandAble, like this:
if (a instanceof ICommandAble) {
commandList.add(a);
}
It's because your adding a into the commandList twice, once for being loggable and again for being monitorable.
You should combine the 2 if statements into 1:
if (a instanceof ILogAble || a instanceof IMonitorAble)
{
commandList.add(a);
}
Add the instances to a hashset not a list so that an instance would be present once even if it added more than once
As the other answers already explained, what you want is not possible with casting. You will need to use multiple lists. You can organize this lists in a Map to make it more maintainable, but maybe you should rethink your design.
Here a way how you could easily use multiple lists:
public interface ICommandAble {}
public interface ILogAble extends ICommandAble {}
public interface IMonitorAble extends ICommandAble {}
public abstract class ClassAbs {}
public class A extends ClassAbs implements IMonitorAble, ILogAble {}
public List<ICommandAble> getList(LinkedHashMap<Class<? extends ICommandAble>, List<ICommandAble>> commandList,
Class<? extends ICommandAble> clazz) {
if (commandList.get(clazz) != null)
return commandList.get(clazz);
ArrayList<ICommandAble> l = new ArrayList<>();
commandList.put(clazz, l);
return l;
}
public void test() {
A a = new A();
LinkedHashMap<Class<? extends ICommandAble>, List<ICommandAble>> commandList = new LinkedHashMap<>();
if (a instanceof ILogAble)
getList(commandList, ILogAble.class).add(a);
if (a instanceof IMonitorAble)
getList(commandList, IMonitorAble.class).add(a);
for (Class<? extends ICommandAble> clazz : commandList.keySet())
for (ICommandAble item : commandList.get(clazz)) {
if (clazz.equals(IMonitorAble.class))
log("is monitorable");
if (clazz.equals(ILogAble.class))
log("is logable");
}
}
Related
I have two simple classes that extends an interface:
public interface Product
{
}
public class ProductA implements Product
{
}
public class ProductB implements Product
{
}
I have two 'services' classes: ServiceA and ServiceB. Each one works with one of the Product classes defined before. Both implements Service interface.
public interface Service<T extends Product>
{
public void print (T product);
}
public class ServiceA implements Service<ProductA>
{
public void print (ProductA product)
{
System.out.println("Product A");
}
}
public class ServiceB implements Service<ProductB>
{
public void print (ProductB product)
{
System.out.println("Product B");
}
}
I would like to developed a factory to instantiate my services:
[BEFORE I FOUND SOLUTION]
public class FactoryService
{
public static Service<? extends Product> getService (String serviceType)
{
Service<? extends Product> s = null;
if ("1".equals(serviceType))
s = new ServiceA();
else if ("2".equals(serviceType))
s = new ServiceB();
return s;
}
}
[SOLUTION]
public static <T> T getService (Type targetType)
{
T service = null;
if (!targetType.getClass().isInstance(Product.class))
throw new RuntimeException();
if (ProductA.class.getTypeName().equals(targetType.getTypeName()))
service = (T) new ServiceA();
else if (ProductB.class.getTypeName().equals(targetType.getTypeName()))
service = (T) new ServiceB();
return service;
}
When I tried to use my factory I get compile errors:
[BEFORE I FOUND SOLUTION]
public static void main(String[] args)
{
Product pA = new ProductA();
Product pB = new ProductB();
Service<? extends Product> service = FactoryService.getService("1");
service.print(pA);
}
[SOLUTION]
public static void main(String[] args)
{
Product pA = new ProductA();
Product pB = new ProductB();
Service<Product> service = FactoryService.getService(pA.getClass());
service.print(pA);
service = FactoryService.getService(pB.getClass());
service.print(pB);
// No compilation errors
}
The error says:
The method print(capture#5-of ? extends Product) in the type Service<capture#5-of ? extends Product> is not applicable for the arguments (Product).
How can I solve this problem?
Thanks
When you want to use generics in declaring a type, you shouldn't use <? extends Type> as Jesper pointed out. Instead you should use <Type>. In your case, replace Service<? extends Product> with Service<Product>.
Now you'll get another error:
Type mismatch: cannot convert from ServiceA to Service<Product>
The solution I suggest is instead of defining ServiceA and ServiceB, to just use the Service#print method and check the generic type, then do what is necessary. The FactoryService#getService method is not needed in this case.
Spoiler for my solution (try without it first):
public class Service<T extends Product> {
public void print(T product) {
if (product instanceof ProductA) {
System.out.println("Product A");
} else if (product instanceof ProductB) {
System.out.println("Product B");
}
}
}
I have two classes A and B and they both have a common field in them, and I want to create a function in which if I pass Class A object then I want to set that common field value to the passed value and if I pass Class B object then I want to set that common field value to the passed value. Can anyone please tell me how can I do this, I am new to Java Generic Classes.
Otherwise I would have to make two different functions OR I would have to make an if and else which would decide that passed object belongs to which class ??
Class A
public class A{
int footer;
public void setFooter(int fo) {
footer = fo;
}
}
Class B
public class B{
int footer;
public void setFooter(int fo) {
footer = fo;
}
}
Class D
public class D{
public void change_footer(T generic_param, int value) {
generic_param.setFooter(value);
}
}
Class HelloWorld
public class HelloWorld{
public static void main(String []args){
Here I want to call
A a = new A();
new D().change_footer(a, 5);
B b = new B();
new D().change_footer(b, 5)
}
}
Thank You
And if I got all of the question wrong, and nor A nor B are generic, AND the type of field is fixed.
then you mean something like:
class D {
/*public <T extends Super> would be muuuch nicer here as well!*/
public /*static*/ <T> void change_footer(T obj, int data) {
//otherwise, you could just cast to Super...and set dat field.
if (obj instanceof A) {
((A) obj).setField(data);
} else if (obj instanceof B) {
((B) obj).setField(data);
} // else ... ?
}
}
Original answer:
Easy peasy (the "straight forward" implementation produces the desired results.):
class A<T> {
T daField;
public void setField(T pField) {
daField = pField;
}
public T getField() {
return daField;
}
}
class B<T> extends A {//empty
}
class Test {
public static void main(String... args) {
B<Object> testB1 = new B<>(); //
testB1.setField(new Object());
System.out.println(testB1.getField());
B<String> testB2 = new B<>();
testB2.setField("blah blah");
System.out.println(testB2.getField());
B<Integer> testB3 = new B<>();
testB3.setField(42);
System.out.println(testB3.getField());
}
}
System.out:
java.lang.Object#6d06d69c
blah blah
42
It get's (little) more complicated, when you want to instantiate Ts ...but still possible/other question. :)
Edit to your comment:
If there's only one common field, then why not:
/*abstract */class Super<T> {
T daField;
public void setField(T pField) {
daField = pField;
}
public T getField() {
return daField;
}
}
? ...and:
class A<T> extends Super { ... }
class B<T> extends Super { ... }
I'm trying to study the difference between deep copy and shallow copy in Java. I created an example where I use a generic class OrganizedGroup to manage Enterprizes,Music groups and others. Then, I create a container with different groups of type OrganizedGroup.
import java.util.ArrayList;
interface Leader {
public void command();
}
class Singer implements Leader {
#Override
public void command() {
System.out.println("I'm a singer");
}
}
class Boss implements Leader {
#Override
public void command() {
System.out.println("I'm a boss");
}
}
class OrganizedGroup<T extends Leader> {
private T mandador;
public OrganizedGroup(T m) {
mandador = m;
}
public void posturaOficial() {
mandador.command();
}
public void setLeader(T m){
mandador = m;
}
}
class MusicGroup extends OrganizedGroup<Singer> {
public MusicGroup(Singer c) {
super(c);
}
public void playMusica() {
System.out.println("Playing music....");
}
}
class Enterprize extends OrganizedGroup<Boss> {
public Enterprize(Boss j) {
super(j);
}
public void makeDeal() {
System.out.println("Making a deal....");
}
}
public class Pruebas {
public static void main(String[] args) {
ArrayList<OrganizedGroup<? extends Leader>> go = new ArrayList();
OrganizedGroup<? extends Leader> g = new MusicGroup(new Singer());
go.add(g);
g = new Enterprize(new Boss());
go.add(g);
for (OrganizedGroup j : go) {
j.posturaOficial();
}
OrganizedGroup< ? extends Leader> t = go.get(0);
t.setLeader(new Singer()); //compile error
}
}
On the last line I try to modify the leader of the first group ( I need to do that in order to see the difference between shallow copy and deep copy (say I cloned go into go2)) however it gives the following compile error:
Singer cannot be converted to CAP#1 where CAP#1 is a fresh
type-variable: CAP#1 extends Leader from capture of ? extends Leader
I need answer to solve this problem, not an explanation of the compile error but more importantly of what is conceptually wrong, what should be change and why.
Please,don't focus on bad design matters I actually made a different implementation of this (OrganizedGroup being abstract) but I need to solve this problem to practise with generics.
There is no need to write:
OrganizedGroup< ? extends Leader> t = go.get(0);
Instead I wrote just:
OrganizedGroup t = go.get(0)
and the same can be done in all main's sentences. No compile error.
java.lang.Class.getInterfaces returns all directly implemented interfaces ie doesn't walk the class tree to get all interfaces of all parent types. eg For example the hierarchy
public interface A {}
public interface B {}
public interface C extends B {}
public class Foo implements A {} // Foo.class.getInterfaces() returns [A]
public class Bar implements C {} // Bar.class.getInterfaces() returns [C], note B is not included.
For Bar I would like to get [B, C], but for any arbitrary tree depth.
I could write this myself, but I'm sure a library must exist that does this already, any ideas?
Apache Commons Lang has method you need: ClassUtils.getAllInterfaces
Guava Solution:
final Set<TypeToken> tt = TypeToken.of(cls).getTypes().interfaces();
This is a much more powerful and cleaner reflection API than the ancient Apache stuff.
Don't forget, Spring Framework has many similar util classes like Apache Commons Lang. So there is: org.springframework.util.ClassUtils#getAllInterfaces
public interface A {}
public interface B {}
public interface E extends B{ }
public class C implements A{}
public class D extends C implements E{}
public class App {
public static void main(String[] args) {
final List<Class<?>> result = getAllInterfaces(D.class);
for (Class<?> clazz : result) {
System.out.println(clazz);
}
}
public static List<Class<?>> getAllInterfaces(Class<?> clazz) {
if (clazz == null) {
System.out.println(">>>>>>>>>> Log : null argument ");
return new ArrayList<>();
}
List<Class<?>> interfacesFound = new ArrayList<>();
getAllInterfaces(clazz, interfacesFound);
return interfacesFound;
}
private static void getAllInterfaces(Class<?> clazz,
List<Class<?>> interfacesFound) {
while (clazz != null) {
Class<?>[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (!interfacesFound.contains(interfaces[i])) {
interfacesFound.add(interfaces[i]);
getAllInterfaces(interfaces[i], interfacesFound);
}
}
clazz = clazz.getSuperclass();
}
}
}
I was wondering how to design a system in which I have a class Super and a couple of classes that are subclasses of Super (let's say Sub1, Sub2, Sub3) and I want a class Cool. Now there are two things I want to have:
Sub1 and Sub2 can be Cool's, Sub3 can never be cool.
I must be able to have a List in which there can be Sub1's and Sub2's, if they are cool. If for example I make an object of Sub1 and it is cool I can put it in the list, if it's not it cannot be in the list.
Any suggestions? Hints?
Arne's answer kind of does what you want, but I find it overly complicated. Maybe I'm missing something? Why not just:
class Super { }
interface Cool { boolean isCool(); }
class CoolImpl extends Super implements Cool {
private boolean cool;
public CoolImpl(boolean cool) { this.cool = cool; }
public boolean isCool() { return this.cool; }
}
class Sub1 extends CoolImpl { }
class Sub2 extends CoolImpl { }
class Sub3 extends Super { }
class CoolList extends ArrayList<Cool> {
public boolean add(Cool cool) {
if (!cool.isCool()) {
return false;
}
return super.add(cool);
}
}
Maybe something like this:
class Super {}
interface Cool { boolean isCool(); }
class IsCool implements Cool {
public boolean isCool() { return true; }
}
class NotCool impolements Cool {
public boolean isCool() { return false; }
}
interface CoolSupporter {
boolean isCool();
Cool getCool();
}
class Sub1 extends Super implements CoolSupporter {
private Cool cool;
public Sub1() { this(new NotCool()); }
public Sub1(Cool cool) { this.cool = cool; }
public boolean isCool() { this.cool.isCool(); }
public Cool getCool() { return this.cool; }
}
class Sub2 extends Super implements CoolSupporter {
private Cool cool;
public Sub1() { this(new NotCool()); }
public Sub1(Cool cool) { this.cool = cool; }
public boolean isCool() { this.cool.isCool(); }
public Cool getCool() { return this.cool; }
}
class Sub3 extends Super {}
class CoolList {
private List<CoolSupporter> list = new ArrayList<CoolSupporter>();
public void add(CoolSupporter coolSupporter) {
if (coolSupporter.isCool()) {
list.add(coolSupporter);
} else {
throw new UncoolException();
}
}
}
You can create an marker interface say cool.
Let class Sub1 and Sub2 implements this interface
and before adding to the list check for instance of cool
may be this can help.
You can't have a class optionally belonging to a type in Java. Though you may subclass Sub1, with one subclass implementing an interface Cool and the other not:
class Super { }
interface Cool { }
class Sub1 extends Super { }
class Sub1Cool extends Sub1 implements Cool { }
class Sub2 extends Super { }
class Sub2Cool extends Sub2 implements Cool { }
class Sub3 extends Super { }
class CoolList extends ArrayList<Super> {
public boolean add(Super sup) {
if (!(sup instanceof Cool)) {
return false;
}
return super.add(cool);
}
}
You might also discard the Cool concept and use a visitor pattern:
class Super {
public boolean addTo(List<Super> coolList) {
if (canBeAddedToCoolList()) {
return coolList.add(this);
}
return false;
}
protected boolean canBeAddedToCoolList() {
return false;
}
}
class Sub1 extends Super {
protected boolean canBeAddedToCoolList() {
// check logic to allow/disallow addition
}
}
IMO, you need to have a overrided List (Say MyList, that overrides add()).
In add(), Check if the object you are adding is Cool, if it is so, then add it part of the list. If not then just gracefully disregard it.
Does this help?
The simplest way you can manage this is to further subclass Sub1 (CoolSub1 and NotCoolSub1) and Sub2 (CoolSub2 and NotCoolSub2).
CoolSub1 and CoolSub2 can then implement Cool ( Cool should be an interface and not a class)
You can then define
List<Cool>
which will accept implementations of Sub1 and Sub2, but only if they implement Cool.