【问题标题】:Self Instantiation in JavaJava中的自实例化
【发布时间】:2009-11-04 16:15:37
【问题描述】:

如何自实例化 Java 类?我不应该使用除其类文件之外的任何文件。还假设在我初始化 NAME 变量的那一行之后,他们在编译时不知道类的名称。

例子:

public class Foo implements Bar {
    public static final String NAME = "Foo";

    private void instantiate()  {
        Bar baz = (Bar)Class.forName(NAME).newInstance();
    }
    //...
}

会出错,因为它需要另一个名为Bar.java 的文件。应该是:

public class Foo {
    /*
    Instantiate self...
    */
}

我加入了这个编程挑战网站,每个问题只允许我提交一个文件。我想为我将用于挑战的所有类创建一个模板类。所以,我的想法是我会这样做:

public class Foo {
    public static final String NAME = "Foo";


    public static void main (String[] args) throws IOException {
        //Instantiate self and call launch(args)
        Foo foo = new Foo();
        foo.launch(args);
        System.exit(0);
    }

    public void launch(String[] args) {
        //...Do actual code here
    }
}

我只想更改类名和 NAME 变量,而不必更改每次使用模板类时实例化类的方式。使用我目前的设置,我将不得不编辑以下行:Foo foo = new Foo();,也许还有下面的行。

您可能还想知道为什么我必须调用 launch() 而不是在 main 方法中执行所有操作。这只是我从我的 Java 讲师那里得到的东西。这也是因为我不能在main() 方法中使用非静态方法。

【问题讨论】:

  • 奇数要求。你不是在找工厂模式吗?或者也许是单例模式(虽然这反过来表明设计不佳)。请详细说明您认为这是解决方案的要求,以便我们提出正确的解决方案。
  • ^^ 注意:我问这个是因为“Foo foo = new Foo()”太明显了,我预计你实际上是在寻找其他东西。

标签: java class object oop instantiation


【解决方案1】:

如果你“实现”任何东西,你需要定义一个接口。要么你导入一个已经存在的,要么你必须创建它。无论哪种方式,如果它是公共类(抽象或非抽象)或接口,它必须在单独的文件中定义。与 C++ 不同,这是 Java 要求。

如果:

public class Foo implements Bar { 
} 

那么您的项目必须至少包含:

Foo.java
Bar.java 

期间。没有如果/和/但是。

但是,如果您的项目中绝对必须只有一个文件,则您必须获得一个字节码生成器并在运行时动态构建您的类和接口。

真正的问题是:您想要完成什么?如果它是单例/工厂,其他人已经回答了这个问题。如果是为了减少编译单元的数量,那你就不走运了。 Java 强制要求您必须这样做。

作者更新后编辑:

Java 允许你做这样的事情:

Foo.java

public class Foo { 
  public static void main(String[] args) { 
    Bar bar = new Bar(); 
  }
}
class Bar { 
   int bar_var; 
   Snafu fu = new Snafu(); 

   public Bar() {  } 
   public int getBarVar() { return bar_var; } 
} 
class Snafu { 
   int var; 
   public Snafu() { } 
}

.... 

这将实现您想要实现的目标。

【讨论】:

  • 您知道内部和嵌套类至少已经存在了 9 年,不是吗?
  • 我知道,但我不能对他做出同样的假设,为什么要混淆他。内部和嵌套类看起来很糟糕,我非常谨慎地使用它们——几乎只有当我有匿名类时(例如 JButton 操作)。不过谢谢你想我。 :-)
【解决方案2】:

您已编辑您的消息并添加了更多信息。那挺好的。现在问题更清楚了。您实际上是在寻找工厂模式。

public abstract class Batch {
    public static void main(String[] args) throws Exception {
        Batch batch = (Batch) Class.forName(args[0]).newInstance();
        batch.run();
    }

    abstract void run();
}

class Foo extends Batch {
    public void run() {
        System.out.println("Executing Foo");
    }
}

class Bar extends Batch {
    public void run() {
        System.out.println("Executing Bar");
    }
}

第一个参数显然是类名。希望这会有所帮助。

【讨论】:

  • 该站点给出了一个特定的文件名来命名每个问题的类/文件。所以问题 1 可能被命名为 School.java 然后第二个问题将被命名为另一个东西,这意味着再次编辑 Batch batch = ... 行。我可能把事情复杂化了,如果我所做的不值得,我可能只需要使用简单的Foo foo = new Foo(); 并使用我的编辑器的搜索和替换功能。
  • 好吧,客户才是王道。忍受它;)
【解决方案3】:

如果您不打算在其上调用多个方法,为什么要存储引用?

public class Foo {
  public static void main(String[] args){
    new Foo().launch(args);
  }
  public void launch(String[] args){
    //...
  }
}

在这里,为每个问题更改文件所需要做的就是更改两行:类头和new 子句!简单!

【讨论】:

    【解决方案4】:

    您需要在 Foo 类中调用其构造函数的 main 方法:

    public static void main(String[] args) {
        Foo f = new Foo(); //the new syntax uses a Constructor, not reflection
        //etc
    

    然后您可以通过java 可执行文件(将调用main 方法)运行该类:

    $> java mypackage.Foo
    

    我不确定您为什么选择实现Bar 接口;从您的问题来看,这似乎是不必要的。

    【讨论】:

      【解决方案5】:

      怎么样?

      public class Foo {
      
          private void instantiate()  {
              Foo foo = Foo.class.newInstance();
          }
          //...
      }
      

      【讨论】:

        【解决方案6】:

        要么在main方法中做:

        public class Foo implements Bar {
            public static final String NAME = "Foo";
        
            public static void main(String[] args) {
                Foo f = new Foo();
            }
        

        或作为初始化器:

        public class Foo implements Bar {
            public static final String NAME = "Foo";
            private static final instance = new Foo();
        
            private void getInstance()  {
                return instance;
            }
        }
        

        【讨论】:

          【解决方案7】:

          这里没有人听说过 Java 中的嵌套/内部类吗?它们从 JDK 1.2 就已经存在,大约 9 年前是新的。

          public class Main {
              public static void main(String[] args) {
                  Bar b = new Foo();
                  b.doSomethingExciting();
              }
              interface Bar {
                  void doSomethingExciting();
              }
              static class Foo implements Bar {
                  public void doSomethingExciting() {
                      System.out.println("Boo!");
                  }
              }
          }
          

          您可以将类嵌套到基本上任意深度,使用包含类作为Module 并包含嵌套类和接口的任意集合。通过包含main(String[]),您可以为您的程序提供一个入口点,它可以从该入口点开始。

          这种方法将为您提供一个源文件,从中可以生成任意数量的类文件。嵌套类都将具有唯一的名称(与在顶层包含多个非公共类的技巧不同)。

          【讨论】:

            猜你喜欢
            • 2021-05-14
            • 1970-01-01
            • 1970-01-01
            • 2011-06-10
            • 1970-01-01
            • 1970-01-01
            • 2013-02-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多