类和属性

时间:2011-11-25 13:27:51

标签: java

我遇到了问题和解决方案,但我并不满意:

我的问题:

我有几个班级和几个“属性”。这些属性不是由方法或逻辑值描述的。它们由具有属性(共同特征)的那些类定义 现在应该形成具有相同属性的类组。这些组可以解释为集合。 子组(子集)的属性不一定与父组(超集)的属性有关 此外,编译器可以在类型正确性上检查这些组。

以前的解决方案:

对于每个组,创建一个(可能为空)接口。具有公共属性的所有类都实现此接口。对于具有此属性的所有类,组和接口都是如此 如果我们现在将一个参数传递给一个只应该带有某个属性的类的方法,那么参数类型就是相应的接口。

示例说明:

所有类都使用方法m1实现接口 对于每个属性,都会创建一个接口,该接口由满足相应属性的所有类实现。例如,属性可以是运行此方法时出现的副作用 因此,接口是一组具有相应属性的类。它可以代表财产。
此外,存在另一种方法m2,其接收特定属性组的对象作为参数。在m2中,调用给定对象的m1。因此,我们知道运行m1时遇到的副作用,或者遇到的属性。无法传递m2没有相应属性/副作用的对象,因为编译器会检查类型是否正确。

代码示例:

(副作用通过输出呈现)

public class Example {
    public static void m2(Property_12 p_12) {
        p_12.m1();
    }

    public static void main() {
        Class_1 c_1 = new Class_1();
        Class_2 c_2 = new Class_2();
        Class_3 c_3 = new Class_3();

        m2(c_1);
        m2(c_2);
        // m2(c_3); // Is not passed by the compiler (wrong type)
    }
}

interface Combinable {
    public void m1();
}

interface Property_12 extends Combinable {}
interface Property_13 extends Combinable {}
interface Property_23 extends Combinable {}
interface Property_123 extends Combinable {}

class Class_1 implements Property_12, Property_13, Property_123 {
    @Override
    public void m1() {
        System.out.println("Property (1,2)");
        System.out.println("Property (1,3)");
        System.out.println("Property (1,2,3)");
    }
}

class Class_2 implements Combinable, Property_12, Property_23, Property_123 {
    @Override
    public void m1() {
        System.out.println("Property (1,2)");
        System.out.println("Property (2,3)");
        System.out.println("Property (1,2,3)");
    }
}

class Class_3 implements Combinable, Property_13, Property_23, Property_123 {
    @Override
    public void m1() {
        System.out.println("Property (1,3)");
        System.out.println("Property (2,3)");
        System.out.println("Property (1,2,3)");
    }
}

为什么我对此解决方案不满意?

我关心的不是特定情况,而是一般问题。 您创建的界面数量很快就会在空中拍摄:

  • 可能的组合=所有类的集合的权力集
  • | P(C)| = 2 ^ | C |

如果在这种情况下设置接口/组,根据特定系统, 必须有一种方法来解决整个事情比为每个组编写一个接口更优雅。努力创建2 ^ | C |界面已经非常庞大,至少应该可以减少界面 如果您现在想要总结几个属性,那么您将再次面临同样的问题。因此,您必须为接口创建任何接口组合:

  • | P(P(C))| = 2 ^ 2 ^ | C |

如果你处理了大量的写作,你可以传递具有多个属性的对象。

我希望我的措辞相当清楚,并且会有一个或另一个有趣的提案。

感谢您的努力!


总有2 ^ | C |接口。也许我可以使用代码生成器来创建这些接口。你怎么看待这个想法?


C =要分组的所有类的集合。


重要的是每个类都有一个,两个,......和| C | -1的共同点,以便存在每个可能的“类组合”(=分组)。 此外,没有设置可能出现两次。因此,所有分组的集合是功率集。我的属性是这些分组。


您可以忽略单元素集(和空集),因为它已经是类本身。因此,公式实际上是:

|P(C) \ ({ {x} : x in C } add "empty set")| = |P(C)| - |C| - 1 = 2^|C| - |C| - 1

示例:
| C | = 2 => | P(C)| - | C | - 1 = 2 ^ | C | - | C | - 1 = 2 ^ 2 - 2 - 1 = 4 - 2 - 1 = 1(=分组/接口)

Class_1 --|
          |--> P_12
Class_2 --|

示例:
| C | = 3 => | P(C)| - | C | - 1 = 2 ^ | C | - | C | - 1 = 2 ^ 3 - 3 - 1 = 8 - 3 - 1 = 4(=分组/接口)

       |-- Class_1 --|
       |             |-- P_12 --|
       |   Class_2 --|          |-- P_123
P_13 --|      |______           |
   |   |             |-- P_23 --|
   |   |-- Class_3 --|          |
   |____________________________|

具体示例:

          |-- PolarBear --|
          |               |--> White --|
          |   Swan -------|            |--> Animal
Mammal <--|     |_________             |
   |      |               |--> Fly ----|
   |      |-- Bat --------|            |
   |___________________________________|

代码:

public class AnimalsWithProperties {

    // It can be passed only white mammal.
    public static void whiteAnimal(White w) {
        w.shout();
    }

    // All animal can be passed
    public static void animalShout(Animal a) {
        a.shout();
    }

    public static void main(String[] args) {
        PolarBear e = new PolarBear();
        Swan s = new Swan();
        Bat f = new Bat();

        whiteAnimal(e);
        whiteAnimal(s);
        // whiteAnimal(f); // Error
        animalShout(e);
        animalShout(s);
        animalShout(f);
    }

}

interface HasAnimalProperty {
    public void shout();
}

interface White extends Animal {}
interface Fly extends Animal {}
interface Mammal extends Animal {}
interface Animal extends HasAnimalProperty {}

class PolarBear implements White, Mammal {
    @Override
    public void shout() {
        System.out.println("PolarBear: I am a white Mammal.");
    }
}

class Swan implements White, Fly {
    @Override
    public void shout() {
        System.out.println("Swan: I am white and can fly.");
    }
}

class Bat implements Fly, Mammal {
    @Override
    public void shout() {
        System.out.println("Bat: I am a flying mammal.");
    }
}

3 个答案:

答案 0 :(得分:2)

我不会将属性本身视为泛型类。这听起来像为不确定的收益增加了很多复杂性。如果要传递特定数量的属性,可以使用普通方法。

object.method(p1,p2,p3,p4,p5);

如果要传递大量参数,可以将它们包含在逻辑参数组中。但是,您可以自行决定什么是逻辑组。

答案 1 :(得分:2)

如果您希望编译器验证正确性,那么Java似乎并不适合您正在寻找的内容。

你可能能够通过注释和一些反思检查来实现一个不那么繁琐的解决方案,这可以自动化,尽管很痛苦,但是这不符合你让编译器进行检查的目标。

自动化反射检查的一种可能的替代方法可能是使用方法句柄来处理一个聪明的解决方案,但我不能就如何使其工作提供真正的建议。也许如果您使用属性注释对象,然后将函数/方法注释为接受具有该属性的内容,则可以将调度系统连接到将在运行时检查的方法句柄框架。不同于反射的疼痛,可能是较小的,但同样仍然是运行时检查。

答案 2 :(得分:1)

如果您想将这些属性视为接口,我会强烈考虑寻找一种自动生成类和/或接口的方法。

我个人会看一下Clojure生成interfacesclasses的方法,它们可以让你传递一个字符串列表来生成接口。然后,您可以轻松地将生成的类包装到jar中,并将其用于项目的其余部分,这可以用Java(或Clojure以这种方式)编写。自动生成可能类的幂集也会相对简单。

另一种符合上述要求的可能性是使用字节码生成器来生成接口和类,但我认为使用上述内容会更简单。