Java接口中的静态工厂方法

14

我正在阅读《Effective java》这本书,其中第一项是关于使用静态工厂方法而不是公共构造函数的。我的疑问是,如果我在指定一个接口时,如何在接口中指定静态工厂方法?因为Java不支持在接口中定义静态方法。该书提到创建一个非可实例化类包含公共静态工厂方法。但是这些方法如何访问实现类的私有构造函数呢?

该书指出,如果您要定义一个接口类型,请创建一个非可实例化类Types,并将静态工厂方法包含在该类中。但是,在Types类中定义的方法如何访问接口类型的具体实现的私有构造函数呢?

编辑:- 下面的句子摘自该书,请解释其含义

"接口不能有静态方法,因此按惯例,接口Type的静态工厂方法被放置在一个非可实例化类(项目4)中,命名为Types"

编辑:- 取自Joshua Bloch的Effective Java:项目1-静态工厂方法

     public interface Foo{ //interface without plural 's' (question 1)
     public void bar();
 }
 public abstract class Foos(){ // abstract factory with plural 's' (question 1)
    public static Foo createFoo(){
        return new MyFoo();
    }
    private class MyFoo implements Foo{ // a non visible implementation (question 2)
       public void bar(){}
    }
 }

我的问题是,静态方法createFoo()如何调用私有构造函数MyFoo


1
这应该能帮助您理解:https://dev59.com/V2025IYBdhLWcg3wPzbL - user1933888
这样翻译可以使概念更加清晰,但我的疑问仍然存在。静态方法createFoo()如何调用MyFoo的私有构造函数? - Manu Viswam
你引用教科书的内容有误。实际段落应该是:
在Java 8之前,接口不能拥有静态方法。按照惯例,接口Type的静态工厂方法被放置在一个不可实例化的伴随类(第4条)Types中。
- mohsen Lzd
@mLotfizad 但是这是指哪个Types类呢?我在java.sql.Types中找不到任何工厂方法。 - pawel_j
5个回答

8
你可以将工厂定义为返回接口,但在内部它会创建一个具体类。
例如:
public Interface I { }

private class Impl implements I {
}

I buildI() {
    return new Impl();
}

关键是使用包私有的实现(甚至如果它们是内部类,则使用私有构造函数),然后只有工厂可以构建它们。
这种方法的力量在于,工厂可以根据要求构建适当的实现,所有这些都对用户不可见。例如,当您使用工厂创建一个EnumSet时,会根据所构建的Enum中有多少个条目而有多个内部实现。 使用长整型位字段的超快版本用于具有少于64个条目的Enums,对于更长的枚举使用较慢的版本。
为了指定工厂的接口,您需要做的就是:
public interface Factory {
   I buildI();
}

现在人们可以使用setFactory(new FactoryImpl())来调用您,然后您可以调用factory.buildI(),他们的代码将返回具体实现。
您可以进一步使用泛型:
public interface GenericFactory<T> {
    T buildInstance();
}

然后你的setFactory变成了:
public void setFactory(GenericFactory<I> factory);

创建一个工厂,他们需要执行以下操作:
public class FactoryImpl implements GenericFactory<I> {
     @override
     I buildInstance() {
        return new impl();
     }
}

但是现在,您可以使用相同的工厂类来创建任何需要工厂的东西,只需更改通用要求。
它能够调用私有构造函数的原因非常简单 - 它在同一类中声明!
在一个Java文件中,您可以创建具有私有构造函数的类。然后在类中定义静态方法,即使它是静态的,它仍然具有访问构造函数所需的特权。
如果工厂和实现位于不同的类中,则它们将放置在同一个包中,并将该方法设置为包私有而不是私有。

我想强制实现者添加静态工厂方法,并指定在接口中预定义的名称。 - Manu Viswam
buidI() 方法应该是静态的,这样用户就可以在不使用工厂对象的情况下调用该方法。 - Manu Viswam
那个我在编辑中引用的句子是什么意思? - Manu Viswam
你可以创建一个带有静态工厂方法的类。但你不能创建一个强制实现它的类创建静态方法的接口。 - Tim B
我在我的回答中添加了几个段落。 - Tim B
显示剩余4条评论

5

1
不完全是这样。它到底是怎么解决的?除了你的答案只有一个链接,你提到的链接并没有涉及到静态工厂方法。 - Amos M. Carpenter

4

一个思考这个问题的方法是以封装包为出发点。考虑以下Java 9+代码:

public interface Engine {

    void start();

    static Engine getEngine(String type) {
        switch (type) {
            case "combustion":
                return new CombustionEngine();
            case "electric":
                return new ElectricEngine();
            default:
                throw new IllegalArgumentException("Unknown engine type : " + type);
        }
    }
}

class CombustionEngine implements Engine {

    CombustionEngine() {
    }

    @Override
    public void start() {
        // injecting fuel and igniting it to create combustion...
    }
}

class ElectricEngine implements Engine {

    ElectricEngine() {
    }

    @Override
    public void start() {
        // electric current from battery flowing through coil in magnetic field...
    }
}

请注意,在实现中的构造函数是包私有的,因此来自不同包的调用者无法直接实例化实现并应该使用工厂方法。
在接口中拥有工厂方法:
- 不需要专门的EngineFactory类(传统的工厂方法设计模式实现)。 - 提醒要"按照接口而非实现编程"。
如果需要,还可以创建单例实现实例,这有助于改善内存占用。
public interface Engine {

    enum EngineType {

        COMBUSTION,
        ELECTRIC;

        private static EnumMap<EngineType, Engine> MAP = new EnumMap<>(EngineType.class);

        static {
            MAP.put(COMBUSTION, new CombustionEngine());
            MAP.put(ELECTRIC, new ElectricEngine());
        }
    }

    void start();

    static Engine getEngine(EngineType type) {
        return EngineType.MAP.get(type);
    }

}

0

它讨论的是在实现中通过静态工厂方法创建对象,而不是在接口中创建。

public Interface MyInterface {
    public void myFunction();
}

public class MyClass implements MyInterface {

    // constructor is made private
    private MyClass() {}

    // Use this to create object instead use static factory
    public static MyClass getInstance() {
        return new MyClass();
    }

    public void myFunction(){
      // your implementation
    } 
}

之前的帖子也谈到了静态工厂方法.. 什么是静态工厂方法?

除了这本书,我还发现下面提到的链接很不错 http://www.javacodegeeks.com/2013/01/static-factory-methods-vs-traditional-constructors.html


0

在接口中无法定义工厂方法,但是你也不能在接口中定义构造函数:接口无法实例化。关键是要在实现接口的类中使用工厂方法而不是在接口本身中使用构造函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接