摘要:動態類型語言的表達力動態語言通常更方便開發較小的項目,因為可以無需聲明類型而節省了很多麻煩。
函數式編程與面向對象編程[2]: 靜態類型語言的表達力 靜態類型語言與動態類型語言
之劍
2016.5.3 21:43:20
像Java或者C#這樣強類型的準靜態語言在實現復雜的業務邏輯、開發大型商業系統、以及那些生命周期很長的應用中也有著非常強的優勢
下面我們就來學習一下這些知識.
有三個名詞容易混淆:
Dynamic Programming Language (動態語言或動態編程語言)
Dynamically Typed Language (動態類型語言)
Statically Typed Language (靜態類型語言)
先定義一下標準:
強類型語言(靜態類型語言)是指需要進行變量/對象類型聲明的語言,一般情況下需要編譯執行。例如C/C++/Java/C#
弱類型語言(動態類型語言)是指不需要進行變量/對象類型聲明的語言,一般情況下不需要編譯(但也有編譯型的)。例如PHP/ASP/Ruby/Python/Perl/ABAP/SQL/JavaScript/Unix Shell等等。
1 靜態類型語言靜態類型語言的類型判斷是在運行前判斷(如編譯階段),比如C#、java就是靜態類型語言,靜態類型語言為了達到多態會采取一些類型鑒別手段,如繼承、接口,而動態類型語言卻不需要,所以一般動態語言都會采用dynamic typing,常出現于腳本語言中.
不過,是不是動態類型語言與這門語言是不是類型安全的完全不相干的,不要將它們聯系在一起!
沒有單元測試或者單元測試沒有達到語句覆蓋或者更強的弱條件組合覆蓋,從而導致某些非正常流程發生時,流經這些未被測試的語句導致語法錯誤而最終整個程序都掛掉.對于業務系統來說,這是非常嚴重的事情。
1.1 優點靜態類型語言的主要優點在于其結構非常規范,便于調試,方便類型安全
現在有這樣一種趨勢,那就是合并動態類型與靜態類型在一種語言中,這樣可以在必要的時候取長補短(下面在第4節中:在Scala語言的特色時介紹).
現在開發效率比以前高多了,主要原因是因為開發語言和編譯器的進步,這個趨勢,只會繼續下去,不要抱著過去的教條不放,java也是在不斷改進的,加了reflection, 加了assert,加了泛型,下個版本,也要加腳本支持了。
其實靜態類型語言,除了性能方面的考量之外,最大的優勢就是可以提供靜態類型安全,編譯器可以檢查你的每一個函數調用是不是書寫了正確的名字,是不是提供了正確類型的參數。這樣一個系統,配合自定義類型的功能,可以讓很多錯誤(比許多人想象的要多)在編譯時就能被發現和定位。
1.2 缺點缺點是為此需要寫更多的類型相關代碼,導致不便于閱讀、不清晰明了。
2 動態類型語言所謂的動態類型語言,意思就是類型的檢查是在運行時做的,比如如下代碼是不是合法的要到運行時才判斷(注意是運行時的類型判斷):
def sum(a, b): return a + b2.1 優點
動態類型語言的優點在于方便閱讀,不需要寫非常多的類型相關的代碼;動態語言代表著更快更簡單的技術大趨勢,因此它將必然成為未來構建軟件和互聯網技術的主角。
動態語言足夠靈活,因此雖然它能夠讓人更集中精力思考業務邏輯的實現,同時也向人工智能的方向走得更近一些,但因此它也更依賴于開發人員本身的技術功底,初學者、中級開發者,難以很好的利用它。 而靜態類型語言,與我們計算機教學的基本科目(c/pascal/basic)延續性比較好,所以對于剛畢業的學生而言,更好接受和學習。
2.2 缺點缺點自然就是不方便調試,命名不規范時會造成讀不懂,不利于理解等。
3 動態類型語言的表達力動態語言通常更方便開發較小的項目,因為可以無需聲明類型而節省了很多麻煩。另外一個答案是,動態類型解除了程序員的束縛,可以最大的 發揮程序員的編程技能,能最有效的利用編程語言里的各種特征和模式。但這些能力都是一把雙刃劍,更多的依賴于程序員的個人才能,如果用不好,或用的過度, 都會產生負面的害處。
觀點一:靜態類型語言因為類型強制聲明,所以IDE可以做到很好的代碼感知能力,因為有IDE的撐腰,所以開發大型系統,復雜系統比較有保障。
對于像Java來說,IDEA/Eclipse確實在代碼感知能力上面已經非常強了,這無疑能夠增加對大型系統復雜系統的掌控能力。但是除了Java擁有這么強的IDE武器之外,似乎其他語言從來沒有這么強的IDE。C#的Visual Studio在GUI開發方面和Wizard方面很強,但是代碼感知能力上和Eclipse差的不是一點半點。至于Visual C++根本就是一個編譯器而已,更不要說那么多C/C++開發人員都是操起vi吭哧吭哧寫了幾十萬行代碼呢。特別是像Linux Kernel這種幾百萬行代碼,也就是用vi寫出來的阿,夠復雜,夠大型,夠長生命周期。
觀點二:靜態語言相對比較封閉的特點,使得第三方開發包對代碼的侵害性可以降到很低。
也就是說靜態類型語言可以保障package的命名空間分割,從而避免命名沖突,代碼的良好隔離性。但是這個觀點也缺乏說服力。
靜態類型語言中C,VB都缺乏良好的命名空間分割,容易產生沖突,但是并沒有影響他們做出來的系統就不夠大,不夠復雜。
而動態類型語言中Ruby/Python/Perl都有比較好的命名空間,特別是Python和Perl,例如CPAN上面的第三方庫成噸成噸的,也從來沒有聽說什么沖突的問題。
誠然像PHP,JavaScript這樣缺乏命名空間的動態語言很容易出現問題,但是這似乎是因為他們缺乏OO機制導致的,而不是因為他們動態類型導致的吧?
說到大型系統,復雜業務邏輯系統,Google公司很多東西都是用python開發的,這也證明了動態類型語言并非不能做大型的復雜的系統。其實我個人認為:
動態類型語言,特別是高級動態類型語言,反而能夠讓人們不需要分心去考慮程序編程問題,而集中精力思考業務邏輯實現,即思考過程即實現過程,用DSL描述問題的過程就是編程的過程,這方面像Unix Shell,ruby,SQL,甚至PHP都是相應領域當之無愧的DSL語言。而顯然靜態類型語言基本都不滿足這個要求。
那靜態類型語言的優勢究竟是什么呢?我認為就是執行效率非常高。所以但凡需要關注執行性能的地方就得用靜態類型語言。其他方面似乎沒有什么特別的優勢。
4 Java語言的問題 4.1 生產力問題??開發調試慢,整體解決方案復雜。這只是相對而言,對于熟練的開發這也許不是問題,作為企業級解決方案而言,Java語言確實比較繁重,即使依賴了很多自動編譯、動態加載、打包并部署的持續集成工具,調試速度依然很慢,與現在的快節奏的開發要求有明顯矛盾. 不過, Java語言的生態不僅僅是語言, 而是JVM. 所以java語言只是JVM生態的一個語言.后來者, 有Scala, Groovy, Fantom, Clojure, Ceylon, Kotlin 和Xtend–mostly等.
??
??
4.2 表達力問題??總體來說Java語言的編寫過程更傾向于過程式的開發,在上一層面上封裝了面向對象的特征和行為,語言的設計是上個世紀九十年代的風格,不是說語言本身不好是其抽象能力不夠,即使到了Java8也只是對Lambda表達式進行了支持,因此引入了Functional Interface也即只有一個方法的接口,和接口里邊的帶具體實現的方法(為了兼容以前的代碼不得不作出的讓步)。Java語言發展到現在其語言特性龐大,如果要完全了解需要幾百頁的文檔,在其發展過程中又只做加法沒又減法,語言慢慢風格混雜,變成了現在這種四不像的狀態,函數式的特性硬生生的嫁接在原來的面向對象特性之上。
??
????一段Java 實體類代碼
?? ?? package com.femon.entity; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; /** * @author 一劍 2015年12月23日 下午4:10:18 */ @Entity @Table(name = "cm_service") public class Service { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String name; private String host; private String requestUrl; private String responseBody; private String expect; private String method; private int paramsType;// 1 : 普通參數請求串query str // 2 : header參數 // 3: 表單方式提交參數 // 4: json post請求 private String paramsMap; private String platform;// H5 Web App private int todaySuccessTimes; private int todayFailTimes; /** * 0 失敗 1 正常 */ private int state; private int totalSuccessTimes; private int totalFailTimes; private Date gmtCreate; private Date gmtModify; /** * platform. * * @return the platform * @since JDK 1.7 */ public String getPlatform() { return platform; } /** * platform. * * @param platform * the platform to set * @since JDK 1.7 */ public void setPlatform(String platform) { this.platform = platform; } public String getParamsMap() { return paramsMap; } public void setParamsMap(String paramsMap) { this.paramsMap = paramsMap; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtModify() { return gmtModify; } public void setGmtModify(Date gmtModify) { this.gmtModify = gmtModify; } public int getTodaySuccessTimes() { return todaySuccessTimes; } public void setTodaySuccessTimes(int todaySuccessTimes) { this.todaySuccessTimes = todaySuccessTimes; } public int getTodayFailTimes() { return todayFailTimes; } public void setTodayFailTimes(int todayFailTimes) { this.todayFailTimes = todayFailTimes; } public int getTotalSuccessTimes() { return totalSuccessTimes; } public void setTotalSuccessTimes(int totalSuccessTimes) { this.totalSuccessTimes = totalSuccessTimes; } public int getTotalFailTimes() { return totalFailTimes; } public void setTotalFailTimes(int totalFailTimes) { this.totalFailTimes = totalFailTimes; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getRequestUrl() { return requestUrl; } public void setRequestUrl(String requestUrl) { this.requestUrl = requestUrl; } public String getResponseBody() { return responseBody; } public void setResponseBody(String responseBody) { this.responseBody = responseBody; } public int getState() { return state; } public void setState(int state) { this.state = state; } public String getExpect() { return expect; } public void setExpect(String expect) { this.expect = expect; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public int getParamsType() { return paramsType; } public void setParamsType(int paramsType) { this.paramsType = paramsType; } /** * TODO * * @see java.lang.Object#toString() */ @Override public String toString() { return "Service [id=" + id + ", name=" + name + ", host=" + host + ", requestUrl=" + requestUrl + ", responseBody=" + responseBody + ", expect=" + expect + ", method=" + method + ", paramsType=" + paramsType + ", paramsMap=" + paramsMap + ", platform=" + platform + ", todaySuccessTimes=" + todaySuccessTimes + ", todayFailTimes=" + todayFailTimes + ", state=" + state + ", totalSuccessTimes=" + totalSuccessTimes + ", totalFailTimes=" + totalFailTimes + ", gmtCreate=" + gmtCreate + ", gmtModify=" + gmtModify + "]"; }}
????
而其實,我們早就知道,雖然這些 getter,setter都是模板化的東西, 可以自動生成的,但是這么冗余的代碼,看起來還是不爽!那些寫Java代碼的牛逼程序員老鳥們里面有一個叫Martin Odersky的, 就整了一個基于JVM的支持函數式又無縫融合OOP的程序設計語言Scala. 這樣的實體類代碼,在Scala中寫作如下:
??
package com.steda.entity import java.util.Date import javax.persistence.{Entity, GeneratedValue, GenerationType, Id} import scala.beans.BeanProperty @Entity class TedaCase { @Id @GeneratedValue(strategy = GenerationType.AUTO) @BeanProperty var id: Long = _ @BeanProperty var name: String = _ @BeanProperty var interfaceId: Long = _ @BeanProperty var paramJsonStr: String = _ @BeanProperty var expectOutput: String = _ @BeanProperty var actualOutput: String = _ @BeanProperty var dataSourceId: Long = _ @BeanProperty var clearSql: String = _ @BeanProperty var tddlApp: String = _ @BeanProperty var state: Integer = _ @BeanProperty var runTimes: Integer = _ @BeanProperty var owner: String = _ @BeanProperty var gmtCreate: Date = _ @BeanProperty var gmtModify: Date = _ }
?我們再看一個Controller層的寫作, Scala可以與Java生態中優秀的框架無縫融合, 比如Spring,Junit. 尤其當今發展勢頭正猛的SpringBoot, JPA等框架, 更是大大的提升了開發測試的生產力.
?
?
4.3 資源消耗問題package com.steda.controller
import java.util.Date
import com.steda.dao.{DataSourceDao, TedaCaseDao}
import com.steda.entity.TedaCase
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.ui.Model
import org.springframework.util.StringUtils
import org.springframework.web.bind.annotation._
import org.springframework.web.servlet.ModelAndView@RestController
@RequestMapping(Array("/tedacase"))
class TedaCaseController @Autowired()(private val tedaCaseDao: TedaCaseDao, private val dataSourceDao: DataSourceDao) {@RequestMapping(Array("/newPage/{interfaceId}"))
def goNewPage(@PathVariable(value = "interfaceId") interfaceId: Long, model: Model): ModelAndView = {model.addAttribute("dataSources", dataSourceDao.findAll()) model.addAttribute("interfaceId", interfaceId) new ModelAndView("/tedacase/new")}
@RequestMapping(Array("/editPage/{id}"))
def goEditPage(model: Model, @PathVariable(value = "id") id: Long): ModelAndView = {val tedacase = tedaCaseDao.findOne(id) model.addAttribute("tedacase", tedacase) model.addAttribute("dataSources", dataSourceDao.findAll()) new ModelAndView("/tedacase/edit")}
@RequestMapping(Array("/detailPage/{id}"))
def goDetailPage(model: Model, @PathVariable(value = "id") id: Long): ModelAndView = {val tedacase = tedaCaseDao.findOne(id) model.addAttribute("tedacase", tedacase) new ModelAndView("/tedacase/detail")}
@RequestMapping(value = {
Array("", "/")}, method = Array(RequestMethod.GET))
def list(model: Model, @RequestParam(value = "tedaCaseName", required = false) tedaCaseName: String): ModelAndView = {var tedaCases: java.util.List[TedaCase] = new java.util.ArrayList[TedaCase] if (!StringUtils.isEmpty(tedaCaseName)) { tedaCases = tedaCaseDao.findByName(tedaCaseName) } else { tedaCases = tedaCaseDao.findAll() } model.addAttribute("tedaCases", tedaCases) model.addAttribute("tedaCaseName", tedaCaseName) new ModelAndView("/tedacase/list")}
@RequestMapping(value = Array("/postnew"), method = Array(RequestMethod.POST))
@ResponseBody
def newOne(@RequestParam(value = "name") name: String, @RequestParam(value = "interfaceId") interfaceId: Long,@RequestParam(value = "paramJsonStr") paramJsonStr: String,@RequestParam(value = "expectOutput") expectOutput: String, @RequestParam(value = "owner") owner: String,@RequestParam(value = "clearSql") clearSql: String, @RequestParam(value = "dataSourceId") dataSourceId: Long, @RequestParam(value = "tddlApp") tddlApp: String) = {val teda = new TedaCase() teda.clearSql = clearSql teda.dataSourceId = dataSourceId teda.interfaceId = interfaceId teda.tddlApp = tddlApp teda.expectOutput = expectOutput teda.owner = owner teda.paramJsonStr = paramJsonStr teda.name = name teda.state = -1 // -1 未執行 0 失敗 1 成功 teda.runTimes = 0 teda.gmtCreate = new Date() teda.gmtModify = new Date() tedaCaseDao.save(teda)}
@RequestMapping(value = Array("/postedit"), method = Array(RequestMethod.POST))
@ResponseBody
def editOne(@RequestParam(value = "id") id: Long, @RequestParam(value = "name") name: String,@RequestParam(value = "paramJsonStr") paramJsonStr: String,@RequestParam(value = "expectOutput") expectOutput: String, @RequestParam(value = "owner") owner: String,@RequestParam(value = "clearSql") clearSql: String, @RequestParam(value = "dataSourceId") dataSourceId: Long, @RequestParam(value = "tddlApp") tddlApp: String) = {val teda = tedaCaseDao.findOne(id) teda.clearSql = clearSql teda.dataSourceId = dataSourceId teda.tddlApp = tddlApp teda.expectOutput = expectOutput teda.owner = owner teda.paramJsonStr = paramJsonStr teda.name = name teda.gmtModify = new Date() tedaCaseDao.save(teda)}
}
??Java語言號稱一次編譯,處處運行,就在于它基于一個需要首先先安裝到他所謂的處處的JDK,通過JVM解析編譯完成后的字節碼來運行,跟操作系統的接口也是在JVM托管的。這樣的好處是JVM可以在實時運行的時候對字節碼進行進一步的優化,也就是大名鼎鼎的JIT,問題是所有的機器上都要安裝可以兼容你的應用程序的JDK,同時JVM啟動消耗的資源不少,起碼數百M,且啟動速度緩慢,同樣的直接編譯成目標操作系統二進制可執行程序的服務,啟動起來消耗的資源小很多且速度快了很多。
在當前差異化的芯片結構中,像C、GO、RUST這種能直接運行于操作系統之上不基于某些龐大繁重的VM之上還是很有必要的,比如物聯網的控制芯片,通常內存也只有幾百K,適用性更強一些,而且現在LLVM架構的編譯器能夠帶來性能的大幅優化,所以編譯依然是一個很好的選擇,除非JIT能夠逆天的達到解釋執行的極限,因此假如我們看到某些語言有Java語言的開發能力和內存安全特性,依然是可以考慮的。
5 Haskell, Go, Scala 5.1 Haskell他雖然很老但是一直是作為學院派函數式語言的代表,其純函數式的特性和簡潔漂亮的語法(糖)讓人看了非常舒服,在接觸了面向過程和面向對象的開發后,如果要學習一種新的寫代碼的思路,面向函數式的語言是目前最好的選擇了,而Haskell有是函數式語言的先驅和集大成者,很多函數式語言的語法都是從Haskell借鑒來的。
作為純函數式語言,Haskell將必然會產生Side-Effect的代碼比如IO操作放到了一起,也即monad風格的部分,而其他的函數可以保證完全的函數式特征,對于同樣的輸入無論運行多少次結果都是一樣的,跟數學中函數的定義一樣嚴格,函數式是一種CPU友好的語言,在當前多核計算機發展狀況下,函數式可以讓程序非常安全的在多個核心上并發而不用擔心大量的數據交互和side-effect, 從而在語言編譯過程中能夠針對并發進行大幅的優化。語言本身的很多寫法也跟數學中的定義很接近,比如定義一個集合
ghci> [x*2 | x <- [1..10]] [2,4,6,8,10,12,14,16,18,20]
看起來很像數學定義,語言可謂優雅漂亮,看著很舒服。作為學院派語言,語言自身設計的要求不可謂不嚴格,完美的闡述了函數式是什么意思,但是語言的復雜度較高,學習曲線很陡峭,很難保證團隊成員的接收程度,也很難招到相關的技術人才。從效率上來講,Haskell可以優化的跟C語言的級別類似,但如果對某些特性不熟悉稍微改動一些就會造成性能的大幅下降,對新手不算友好。
同時在函數式不那么擅長的領域Haskell的商業化程度很低,我們不可能都用Haskell來寫一些語法解釋或者正則解析等,涉及IO的分布式存儲和計算都相對很初級,尤其是對于我們比較感興趣的數據挖掘機器學習領域沒有成熟的解決方案,對于Web項目支持的尚可,有優秀的Yesod框架作為代表。
總的來說,Haskell值的學習但不會在大型的生產環境中使用。
5.2 Scala??Scala語言的出現目的很明確,感覺就是為了替代Java而存在,在Java語言越來越力不從心的今天,能夠有一門語言既繼承了它廣大的生態系統,又能夠在表達能力和開發效率大大改進的情況,可以說是很有希望的。
Scala從一開始就是一門設計良好的語言,幾乎完美的集合了函數式的特性和面向對象的特性,雖然他的函數式不是純函數式。其面向對象的感覺更像Ruby而不是Java,所有的東西都是對象,包括簡單類型例如Int,以及函數本身都是一種對象,這樣在這個層面實現了面向對象和函數式的統一。
Scala運行于JVM之上,能夠無縫的使用所有的原來Java語言所開發的各種庫,語言上作為Java的超集,遷移過來只會更強大而不會打折。
Java8的出現雖然增加了針對集合的stream api以及Lambda表達式這種函數式特性的支持,但只會讓人們覺得Java與Scala更像了,即使Java在以后的發展過程中擁有了所有的Scala的能力.
打個比方一塊歪歪扭扭的經過各種后期焊接所建造起來的機器和一個一開始就有目的的設計出來的結構精密、風格統一、表達高效的機器比較,后者更像前者的重構,而前者雖然如日中天但已經是暮年的四不像,不停的往身上增加各種各樣的功能.
也許Java9會有進步,但現在我看到Java8后反而更傾向于Scala。
Scala的元編程能力可以讓他修改自己的語言定義,不只是實現某些業務邏輯,這樣從符號層面上,scala可以做到自洽,除了核心的一些規則,其他的都可以被自己根據狀態調整所修改,這種能力可以極大的擴展語言自身的能力,當然也帶來了一些負面效果,每學習一種新的包不只是了解他的API,而是學習了一種新的語言,風格可能跟scala大不相同。
強有力的證明,大數據生態系統代表-Spark&Kafka,一個是分布式計算一個是分布式大規模數據吞吐,都證明了Scala的開發能力和效率。
5.3 GoScala的問題其實也有跟Java類似的地方,首先這個語言雖然是重新設計的,但是使用起來復雜度依然很高,對于范型的繼承,+-等范型標注不好理解,
??Go語言目前呈現了很火爆的趨勢,由于其簡單,整個語言的specification也不過十幾頁,最多半天就能夠完全了解并上手寫一些小工具。GO語言最初是希望替代C和C++成為新的系統語言,自帶GC垃圾回收,不過最終更多的是替代了python來開發一些服務或者工具,并沒有成為系統級別的語言。
??Go語言有很多的優點,編譯速度快,有協程和Channel做并發支持和通信,有很多官方的網絡協議的庫,非常適合于寫一些網絡服務,啟動一個http的接口服務只需要幾行代碼。目前github上也有大量的第三方項目使用go語言來開發應用或者擴展go的功能,在使用的時候直接import即可。Go的多返回機制也還不錯,節省了大量的無意義數據結構和不可讀的Map的使用,總的來說Go在其擅長的領域生產力很高,寫起來比較流暢,靜態類型也足夠的安全。目前Docker生態系統里邊的各種工具都是Go來寫的。最新發布的1.5版本使得交叉編譯更加容易,靜態鏈接庫的方式使生成的可執行文件在相同CPU架構的操作系統都能運行,減少了額外查找依賴的問題,對我們現在基本同構的Linux服務器而言,也打到了一次編譯處處運行的目的。同時Go語言在運行時消耗的資源也比Java要小,啟動速度更快,確實是輕量級服務的優選。
??
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65859.html
摘要:通常一個完成的不僅僅包含了還包括了以及相關版本該版本在中使用。基于原型函數先行的語言使用基于原型的的繼承機制,函數是的第一等公民其他相關的語言特性編譯型語言把做好的源程序全部編譯成二進制代碼的可運行程序。 轉載請注明出處,創作不易,更多文章請戳 https://github.com/ZhengMaste... 前言:JavaScript誕生于1995年,它是一門腳本語言,起初的目...
摘要:所有變量的類型在編譯時已知在程序運行之前,因此編譯器也可以推導出所有表達式的類型。像變量的類型一樣,這些聲明是重要的文檔,對代碼讀者很有用,并由編譯器進行靜態檢查。對象類型的值對象類型的值是由其類型標記的圓。 大綱 1.編程語言中的數據類型2.靜態與動態數據類型3.類型檢查4.易變性和不變性5.快照圖6.復雜的數據類型:數組和集合7.有用的不可變類型8.空引用9.總結 編程語言中的數據...
摘要:我們的目標是建立對每一種語言的認識,它們是如何進化的,未來將走向何方。有點的味道是堅持使用動態類型,但唯一還收到合理擁泵的編程語言,然而一些在企業的大型團隊中工作的開發者擇認為這會是的一個缺陷。 為什么我們需要如此多的JVM語言? 在2013年你可以有50中JVM語言的選擇來用于你的下一個項目。盡管你可以說出一大打的名字,你會準備為你的下一個項目選擇一種新的JVM語言么? 如今借助來自...
摘要:原文鏈接有大量平均水平左右的工人可被選擇參與進來這意味著好招人有成熟的大量的程序庫可供選擇這意味著大多數項目都是既有程序庫的拼裝,標準化程度高而定制化場景少開發工具測試工具問題排查工具完善,成熟基本上沒有團隊愿意在時間緊任務重的項目情況 原文鏈接:http://pfmiles.github.io/blog/java-groovy-mixed/ 有大量平均水平左右的工人可被選擇、參與...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
閱讀 1557·2021-11-17 09:33
閱讀 1106·2021-11-12 10:36
閱讀 2418·2019-08-30 15:54
閱讀 2443·2019-08-30 13:14
閱讀 2918·2019-08-26 14:05
閱讀 3294·2019-08-26 11:32
閱讀 3006·2019-08-26 10:09
閱讀 3001·2019-08-26 10:09