首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >摸索Java文化--为什么事情如此沉重?它优化的目的是什么?

摸索Java文化--为什么事情如此沉重?它优化的目的是什么?
EN

Software Engineering用户
提问于 2017-04-26 11:35:23
回答 15查看 28.9K关注 0票数 295

我以前经常用Python编写代码。现在,由于工作原因,我用Java编写代码。我所做的项目相当小,而且可能Python会更好地工作,但是使用Java有充分的非工程理由(我不能详细说明)。

Java语法不是问题,它只是另一种语言。但是除了语法之外,Java还有一种被认为“正确”的文化、一组开发方法和实践。而就目前而言,我完全没有“摸索”这种文化。所以我非常感谢你给我正确的解释或指点。

在我启动的堆栈溢出问题中提供了一个最小的完整示例:https://stackoverflow.com/questions/43619566/returning-a-result-with-several-values-the-java-way/43620339

我有一个任务-解析(从一个字符串),并处理一组三个值。在Python中,它是一个一行程序(tuple),在Pascal或C中是一个5行记录/结构。

根据答案,在Java语法中可以获得相当于struct的结构,在广泛使用的Apache库中可以获得一个三元结构,但是,“正确”的方法实际上是为值创建一个单独的类,包含getters和setter。有人很好地提供了一个完整的例子。它是47行代码(嗯,其中一些行是空白的)。

我明白,一个庞大的开发社区很可能不会“错”。所以这是我理解的一个问题。

Python实践优化了可读性(在这种思想中,这将导致可维护性),并在此之后优化开发速度。C优化资源使用的实践。Java实践优化的目的是什么?我最好的猜测是可伸缩性(每件事都应该处于一个为数百万LOC项目做好准备的状态),但这是一个非常微弱的猜测。

EN

回答 15

Software Engineering用户

发布于 2017-04-26 14:39:39

:Java语言

我相信,所有这些答案都是由于试图将意图归因于Java的工作方式而忽略了这一点。Java的冗长并不是因为它是面向对象的,因为Python和许多其他语言还没有更简洁的语法。Java的冗长之处也不是来自它对访问修饰符的支持。相反,这只是Java是如何设计和发展的。

Java最初是作为带有OO的略有改进的C创建的。因此,Java具有70年代的语法。此外,为了保持向后兼容性,并允许Java经得起时间的考验,Java在添加特性方面非常保守。如果Java在2005年加入了像XML文字这样的时髦特性,当时XML非常流行,那么语言中就会充斥着没有人关心的鬼特性,这也限制了10年后它的发展。因此,Java缺乏许多现代语法来简洁地表达概念。

然而,根本上没有什么能阻止Java采用这种语法。例如,Java 8添加了lambda和方法引用,大大减少了许多情况下的冗长。Java也可以添加对紧凑数据类型声明(如Scala的case类)的支持。但是Java根本没有这么做。请注意,自定义值类型即将出现,此特性可能会引入一种新的语法来声明它们。我想我们会看到的。

Java文化

企业Java开发的历史在很大程度上导致了我们今天所看到的文化。在90年代末/00年代初,Java成为服务器端业务应用程序的一种非常流行的语言。当时,这些应用程序大多是临时编写的,并包含了许多复杂的关注点,如HTTP、数据库和处理XML提要。

在00年代,这些应用程序中的许多都有很多共同点,管理这些关注点的框架(如Hibernate ORM、Xerces XML解析器、JSP和servlet API以及EJB )变得流行起来。然而,虽然这些框架减少了在它们设置为自动化的特定领域中工作的工作量,但它们需要配置和协调。当时,无论出于什么原因,编写框架以满足最复杂的用例是很流行的,因此这些库的建立和集成非常复杂。随着时间的推移,它们随着特征的积累而变得越来越复杂。Java企业开发逐渐变得越来越多,越来越多地将第三方库插入到一起,而很少涉及编写算法。

最终,企业工具的繁琐配置和管理变得相当痛苦,以致于框架(尤其是Spring框架)出现了来管理管理。理论上说,你可以把所有的配置放在一个地方,然后配置工具会配置这些部分并将它们连接在一起。不幸的是,这些“框架框架”增加了更多的抽象和复杂性在整个蜡球之上。

在过去的几年里,越来越多的轻量级库越来越受欢迎。然而,在大型企业框架的发展过程中,整整一代的Java程序员都已经成熟了。他们的角色模型,那些开发框架的人,编写了工厂工厂和代理配置bean加载器。他们不得不每天配置和集成这些怪物。因此,整个社区的文化都效仿了这些框架,并倾向于过度设计。

票数 240
EN

Software Engineering用户

发布于 2017-04-26 15:39:54

我相信我有一个答案,你提出的一点,但没有被其他人提出。

Java优化了编译时程序员错误检测,从不做任何假设

一般来说,Java倾向于在程序员已经明确表达了他的意图之后才推断出关于源代码的事实。Java编译器从不对代码进行任何假设,只会使用推断来减少冗余代码。

这种哲学背后的原因是程序员仅仅是人。我们写的并不总是我们真正想要程序做的事情。Java语言试图通过强制开发人员始终显式声明它们的类型来缓解其中的一些问题。这只是一种重复检查所编写的代码是否实现了预期目标的一种方式。

其他一些语言通过检查预条件、后置条件和不变量(虽然我不确定它们在编译时会这样做)来进一步推进这种逻辑。对于程序员来说,这些都是更极端的方法,让编译器重新检查自己的工作。

在您的示例中,这意味着为了使编译器能够保证您实际上正在返回您认为正在返回的类型,您需要向编译器提供该信息。

在Java中,有两种方法可以这样做:

  1. 使用Triplet<A, B, C>作为返回类型(实际上应该在java.util中,我无法解释为什么不返回。特别是因为JDK8引入了FunctionBiFunctionConsumerBiConsumer等。看起来PairTriplet至少是有道理的。但我离题了)
  2. 为此目的创建您自己的值类型,其中每个字段的名称和类型都是正确的。

在这两种情况下,编译器都可以保证您的函数返回它声明的类型,并且调用方认识到每个返回字段的类型,并相应地使用它们。

有些语言同时提供静态类型检查和类型推断,但这为类型不匹配的微妙类打开了大门。其中,开发人员打算返回某个类型的值,但实际上返回另一个类型,而编译器仍然接受代码,因为通过协同关联,函数和调用方都只使用可以应用于预期类型和实际类型的方法。

考虑在类型记录(或流类型)中使用类型推断而不是显式类型的情况。

代码语言:javascript
复制
function parseDurationInMillis(csvLine){
    // here the developer intends to return a Number, 
    // but actually it's a String
    return csv.firstField();
}

// Compiler infers that parseDurationInMillis is String, so it does
// string concatenation and infers that plusTwoSeconds is String
// Developer actually intended Number
var plusTwoSeconds = 2000 + parseDurationInMillis(csvLine);

当然,这是一个愚蠢的琐碎案例,但它可能会更加微妙,导致很难调试问题,因为代码看起来是正确的。这种问题在Java中是完全避免的,而这正是整个语言所围绕的问题。

请注意,按照正确的面向对象原则和域驱动建模,链接问题中的持续时间解析用例也可以作为java.time.Duration对象返回,这比上述两种情况都要明显得多。

票数 74
EN

Software Engineering用户

发布于 2017-04-26 14:59:40

Java和Python是我使用最多的两种语言,但我来自相反的方向。也就是说,在我开始使用Python之前,我就深入到了Java世界,所以我可能能够提供帮助。我认为“为什么事情如此沉重”这个更大的问题的答案可以归结为两件事:

  • 两者之间的发展成本就像长气球动物气球中的空气一样。你可以挤压气球的一部分,另一部分膨胀。Python倾向于挤压早期的部分。Java压缩了后面的部分。
  • Java仍然缺乏一些可以消除这种重量的特性。Java 8在这方面做了很大的让步,但是文化还没有完全消化这些变化。Java还可以使用更多的东西,比如yield

Java‘优化’了高价值的软件,这些软件将由大型团队维护多年。我有用Python编写东西的经验,一年后再看它,并且对我自己的代码感到困惑。在Java中,我可以查看其他人代码的微小片段,并立即知道它的功能。在Python中,您不能真正做到这一点。这并不是像你似乎意识到的那样更好,他们只是有不同的代价。

在您提到的具体案例中,没有元组。简单的解决方案是创建一个具有公共价值的类。当Java第一次出现时,人们经常这样做。这样做的第一个问题是,这是一个维护头痛。如果您需要添加一些逻辑或线程安全性,或者希望使用多态,那么您至少需要接触与'tuple-esque‘对象交互的每个类。在Python中,有一些解决方案,比如__getattr__等,所以没有那么可怕。

尽管如此,这里还是有一些坏习惯。在这种情况下,如果您想要一个元组,我会问您为什么要使它成为一个可变的对象。您应该只需要getter(顺便提一句,我讨厌get/set约定,但事实就是如此)。我确实认为,在Java中的私有或包私有上下文中,一个裸类(可变或不可变)是有用的。也就是说,通过将项目中的引用限制在类中,您可以在不更改类的公共接口的情况下,稍后根据需要重构。下面是如何创建一个简单的不可变对象的示例:

代码语言:javascript
复制
public class Blah 
{
  public static Blah blah(long number, boolean isInSeconds, boolean lessThanOneMillis)
  {
    return new Blah(number, isInSeconds, lessThanOneMillis);
  }

  private final long number;
  private final boolean isInSeconds;
  private final boolean lessThanOneMillis;

  public Blah(long number, boolean isInSeconds, boolean lessThanOneMillis)
  {
    this.number = number;
    this.isInSeconds = isInSeconds;
    this.lessThanOneMillis = lessThanOneMillis;
  }

  public long getNumber()
  {
    return number;
  }

  public boolean isInSeconds()
  {
    return isInSeconds;
  }

  public boolean isLessThanOneMillis()
  {
    return lessThanOneMillis;
  }
}

这是我使用的一种模式。如果您没有使用IDE,则应该启动。它将为您生成getter(如果需要的话还会生成setter),这样就不会那么痛苦了。

如果我不指出已经有一种类型似乎能满足您的大多数需求,这里,我会感到疏忽。撇开这一点不说,您使用的方法不太适合Java,因为它利用了Java的弱点,而不是它的优点。以下是一个简单的改进:

代码语言:javascript
复制
public class Blah 
{
  public static Blah fromSeconds(long number)
  {
    return new Blah(number * 1000_000);
  }

  public static Blah fromMills(long number)
  {
    return new Blah(number * 1000);
  }

  public static Blah fromNanos(long number)
  {
    return new Blah(number);
  }

  private final long nanos;

  private Blah(long nanos)
  {
    this.nanos = nanos;
  }

  public long getNanos()
  {
    return nanos;
  }

  public long getMillis()
  {
    return getNanos() / 1000; // or round, whatever your logic is
  }

  public long getSeconds()
  {
    return getMillis() / 1000; // or round, whatever your logic is
  }

  /* I don't really know what this is about but I hope you get the idea */
  public boolean isLessThanOneMillis()
  {
    return getMillis() < 1;
  }
}
票数 50
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/347852

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档