当前位置:  开发笔记 > 编程语言 > 正文

与java中的静态字段接口以共享"常量"

如何解决《与java中的静态字段接口以共享"常量"》经验,为你挑选了5个好方法。

我正在寻找一些开源Java项目来进入Java,并注意到它们中有很多都有某种"常量"接口.

例如,processing.org有一个名为PConstants.java的接口,大多数其他核心类实现了这个接口.界面充满了静态成员.有这种方法的理由,还是这被认为是不好的做法?为什么不在有意义的地方使用枚举,或者使用静态类?

我发现使用接口允许某种伪"全局变量"很奇怪.

public interface PConstants {

  // LOTS OF static fields...

  static public final int SHINE = 31;

  // emissive (by default kept black)
  static public final int ER = 32;
  static public final int EG = 33;
  static public final int EB = 34;

  // has this vertex been lit yet
  static public final int BEEN_LIT = 35;

  static public final int VERTEX_FIELD_COUNT = 36;


  // renderers known to processing.core

  static final String P2D    = "processing.core.PGraphics2D";
  static final String P3D    = "processing.core.PGraphics3D";
  static final String JAVA2D = "processing.core.PGraphicsJava2D";
  static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
  static final String PDF    = "processing.pdf.PGraphicsPDF";
  static final String DXF    = "processing.dxf.RawDXF";


  // platform IDs for PApplet.platform

  static final int OTHER   = 0;
  static final int WINDOWS = 1;
  static final int MACOSX  = 2;
  static final int LINUX   = 3;

  static final String[] platformNames = {
    "other", "windows", "macosx", "linux"
  };

  // and on and on

}

Dan Dyer.. 156

这通常被认为是不好的做法.问题是常量是实现类的公共"接口"(缺少更好的词)的一部分.这意味着实现类将所有这些值发布到外部类,即使它们仅在内部需要.常量在整个代码中激增.一个例子是Swing中的SwingConstants接口,它由几十个类实现,这些类都将所有常量(甚至是它们不使用的常量)"重新导出" 为自己的常量.

但是,不要只听我的话,Josh Bloch也说它很糟糕:

常量接口模式是接口的不良使用.类在内部使用一些常量是一个实现细节.实现常量接口会导致此实现细节泄漏到类的导出API中.类的用户实现一个常量接口并不重要.事实上,它甚至可能使他们感到困惑.更糟糕的是,它代表了一种承诺:如果在将来的版本中修改了类以便它不再需要使用常量,它仍然必须实现接口以确保二进制兼容性.如果非最终类实现了一个常量接口,那么它的所有子类的命名空间都会受到接口中常量的污染.

枚举可能是更好的方法.或者您可以简单地将常量作为公共静态字段放在无法实例化的类中.这允许另一个类访问它们而不会污染自己的API.



1> Dan Dyer..:

这通常被认为是不好的做法.问题是常量是实现类的公共"接口"(缺少更好的词)的一部分.这意味着实现类将所有这些值发布到外部类,即使它们仅在内部需要.常量在整个代码中激增.一个例子是Swing中的SwingConstants接口,它由几十个类实现,这些类都将所有常量(甚至是它们不使用的常量)"重新导出" 为自己的常量.

但是,不要只听我的话,Josh Bloch也说它很糟糕:

常量接口模式是接口的不良使用.类在内部使用一些常量是一个实现细节.实现常量接口会导致此实现细节泄漏到类的导出API中.类的用户实现一个常量接口并不重要.事实上,它甚至可能使他们感到困惑.更糟糕的是,它代表了一种承诺:如果在将来的版本中修改了类以便它不再需要使用常量,它仍然必须实现接口以确保二进制兼容性.如果非最终类实现了一个常量接口,那么它的所有子类的命名空间都会受到接口中常量的污染.

枚举可能是更好的方法.或者您可以简单地将常量作为公共静态字段放在无法实例化的类中.这允许另一个类访问它们而不会污染自己的API.


顺便说一句:您可以使用没有实例的枚举作为无法实例化的类.;)
Enums在这里是一个红鲱鱼 - 或者至少是一个单独的问题.当然应该使用枚举,但如果实现者不需要它们,也应该隐藏它们.
但为什么首先实现这些接口?为什么不将它们用作常量存储库?如果我需要某种全局共享的常量,我不会看到"更清洁"的方法.
@DanDyer是的,但是接口使某些声明隐式存在。像* public static final *只是默认值。为什么要上课?枚举-好吧,这取决于。枚举应为一个实体定义一个可能值的集合,而不是为不同实体定义一个值的集合。
我个人觉得乔希(Josh)打错了球。如果您不希望常量泄漏-无论您放置哪种类型的对象-您都需要确保它们不属于导出代码。接口或类都可以导出。因此,正确的问题不是:我将它们放在哪种类型的对象中,而是如何组织该对象。并且,如果常量在导出的代码中使用,则仍要确保常量一旦导出就可用。因此,以我的拙见,“不良做法”主张无效。

2> Zarkonnen..:

在Java 1.5+中,您可以使用静态导入从另一个类/接口导入常量/静态方法,而不是实现"常量接口":

import static com.kittens.kittenpolisher.KittenConstants.*;

这避免了使您的类实现没有功能的接口的丑陋.

至于有一个类来存储常量的做法,我认为它有时是必要的.某些常量在一个类中没有自然的位置,因此最好将它们放在"中立"的位置.

但是不使用接口,而是使用带有私有构造函数的final类.(使得无法对类进行实例化或子类化,发送一条强大的消息,表明它不包含非静态功能/数据.)

例如:

/** Set of constants needed for Kitten Polisher. */
public final class KittenConstants
{
    private KittenConstants() {}

    public static final String KITTEN_SOUND = "meow";
    public static final double KITTEN_CUTENESS_FACTOR = 1;
}


不,那不是我说的话.我说两个独立的事情.1:使用静态导入而不是滥用继承.2:如果必须有常量存储库,请将其设为最终类而不是接口.
针对该问题发布的代码的问题之一是接口实现仅用于更容易地访问常量.当我看到实现FooInterface的东西时,我希望它会影响它的功能,而上述内容违反了这一点.静态导入可以解决该问题.
Gizmo - 我不是静态导入的粉丝,但他在那里做的是避免使用类名,即ConstClass.SOME_CONST.执行静态导入不会将这些成员添加到您不会从接口继承的类中,他说事实恰恰相反.

3> pleerock..:

我不假装正确的权利,但让我们看看这个小例子:

public interface CarConstants {

      static final String ENGINE = "mechanical";
      static final String WHEEL  = "round";
      // ...

}

public interface ToyotaCar extends CarConstants //, ICar, ... {
      void produce();
}

public interface FordCar extends CarConstants //, ICar, ... {
      void produce();
}

// and this is implementation #1
public class CamryCar implements ToyotaCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

// and this is implementation #2
public class MustangCar implements FordCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

ToyotaCar对FordCar一无所知,而FordCar也不了解ToyotaCar.原则CarConstants应该改变,但......

常量不应该改变,因为车轮是圆形的,而且egine是机械的,但是......未来丰田的研究工程师发明了电子发动机和平轮!让我们看看我们的新界面

public interface InnovativeCarConstants {

          static final String ENGINE = "electronic";
          static final String WHEEL  = "flat";
          // ...
}

现在我们可以改变我们的抽象:

public interface ToyotaCar extends CarConstants

public interface ToyotaCar extends InnovativeCarConstants 

现在如果我们需要更改核心价值,如果我们可以在ENGINE或WHEEL上更改抽象级别的ToyotaCar接口,请不要触及实现

我知道,它不安全,但我仍然想知道你是否考虑过这一点



4> phil_20686..:

在Java中,这种模式有很多仇恨.但是,静态常量的接口有时会有价值.您需要基本满足以下条件:

    这些概念是几个类的公共接口的一部分.

    他们的价值观可能会在未来的版本

    所有实现都使用相同的值至关重要.

例如,假设您正在编写假设查询语言的扩展.在此扩展中,您将使用索引支持的一些新操作来扩展语言语法.例如,您将拥有支持地理空间查询的R-Tree.

所以你用静态常量写一个公共接口:

public interface SyntaxExtensions {
     // query type
     String NEAR_TO_QUERY = "nearTo";

     // params for query
     String POINT = "coordinate";
     String DISTANCE_KM = "distanceInKm";
}

现在,一个新开发人员认为他需要构建一个更好的索引,所以他来构建一个R*实现.通过在他的新树中实现此接口,他保证不同的索引在查询语言中具有相同的语法.此外,如果您后来认为"nearTo"是一个令人困惑的名称,您可以将其更改为"withinDistanceInKm",并且知道所有索引实现都会尊重新语法.

PS:这个例子的灵感来自Neo4j空间代码.



5> 小智..:

鉴于后见之明的优势,我们可以看到Java在很多方面都被打破了.Java的一个主要缺点是对抽象方法和静态最终字段的接口的限制.更新,更复杂的OO语言,如Scala包含特征的接口,这些特性可以(通常也包括)具体方法,这些方法可能具有零(常量!).有关将特征作为可组合行为单元的说明,请参阅http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf.有关Scala中的特征与Java中的接口的比较的简短描述,请参阅http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5.在教授面向对象设计的背景下,简单的规则,比如断言接口永远不应该包含静态字段,都是愚蠢的.许多特征自然包括常量,这些常量恰当是特征支持的公共"接口"的一部分.在编写Java代码时,没有干净,优雅的方式来表示特征,但在接口中使用静态最终字段通常是一个很好的解决方法的一部分.


非常自命不凡,现在已经过时了.
推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有