Scala ve Java: Neden Scala Öğrenmeliyim?
Yayınlanan: 2022-03-11“Scala zor” ifadesinde kuşkusuz bazı gerçekler var, ancak öğrenme eğrisi yatırıma değer. Dilin daha karmaşık özelliklerinden bazıları (birkaçını saymak gerekirse Tuples, Functions, Macros), sonuçta geliştiricinin daha iyi kod yazmasını ve Scala'da programlama yaparak performansı artırmasını kolaylaştırır. Açıkçası, biz programcıyız ve biraz karmaşık olan bir dili öğrenecek kadar akıllı değilsek, yanlış bir işteyiz demektir.
Scala, hem nesne yönelimli hem de işlevsel programlamayı son derece özlü, mantıklı ve olağanüstü güçlü bir dilde birleştiren, güvenli bir JVM dilidir. Bazıları, 2003 yılında piyasaya sürülen Scala'nın düşündükleri kadar yeni olmadığını öğrenince şaşırabilirler. Ancak, özellikle son birkaç yılda Scala'nın önemli bir takipçi kitlesi kazanmaya başlamasıdır. Bu da “Neden Scala?” sorusunu akla getiriyor.
Bu makale, özellikle Java'ya karşı Scala'nın avantajlarını incelemektedir (çünkü Scala, JVM'de çalışacak şekilde yazılmıştır). Scala, “daha iyi bir Java” yaratmaya yönelik tek girişim değil. Kotlin ve Seylan gibi alternatifler de bu yoldan gittiler, ancak öğrenme eğrisini en aza indirmek için sözdiziminde Java dilinin kendisine çok yakın kalmak için temel bir karar verdiler. Bu harika bir fikir gibi görünebilir, ancak sizi en başta “daha iyi bir Java” yaratmak istemenizin nedeni olan aynı Java paradigmalarının bir dizi içinde kalmaya zorladığı için nihayetinde kendi kendini baltalıyor.
Buna karşılık, Scala özellikle daha iyi bir dil olma hedefiyle yaratıldı ve Java'nın geliştirici için kısıtlayıcı, aşırı sıkıcı veya sinir bozucu olduğunu düşündüğü yönlerini ortadan kaldırdı. Sonuç olarak, gerçekten de Scala programlamanın erken öğrenilmesini biraz daha zorlaştırabilecek kod farklılıkları ve paradigma kaymaları vardır, ancak sonuç, kullanımı daha kolay olan ve üretkenliği artıran çok daha temiz ve iyi organize edilmiş bir dildir.
Scala vs. Java: Hangisi Gerçekten Daha Karmaşık?
Java dilinin sadeliği başarısının bir parçası olsa da, ironik bir şekilde karmaşıklığına da katkıda bulunmuştur. Elbette, Java'da hemen hemen her şeyi yazabilirsiniz, ancak bunu yapmak için gereken kod satırları göz korkutucu olabilir. Scala'da programlama ise biraz daha karmaşık bir yapıya sahiptir. Ancak Java'nın 20 "daha basit" satırının yerini alan biraz daha karmaşık tek satırlık bir kod yazabilirseniz, hangisi gerçekten daha karmaşıktır?
Gerçek şu ki, Java genellikle çok ayrıntılıdır. Scala'da derleyici inanılmaz derecede akıllıdır, bu nedenle geliştiricinin derleyicinin çıkarabileceği şeyleri açıkça belirtmesine gerek kalmaz. Örneğin, bu basit “Merhaba Dünya!” ile karşılaştırın. Java'da Scala'ya karşı program:
Java'da Merhaba Dünya:
public class HelloJava { public static void main(String[] args) { System.out.println("Hello World!"); } }
Scala'da Merhaba Dünya:
object HelloScala { def main(args: Array[String]): Unit = { println("Hello World!") } }
Burada iki dil arasında çok büyük bir fark olmasa da, Scala bu basit örnekte bile daha az ayrıntılıdır.
Daha pratik bir örnek için, basit bir Dizeler listesi oluşturmaya bir göz atalım:
Java:
List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3");
Ölçek:
val list = List("1", "2", "3")
Elbette Java'da kodu biraz kısaltmak için bazı hileler var, ancak standart kullanımda değil.
Şimdi, sayılardan oluşan bir dizi listemiz olduğu, ancak bu listeyi bir tamsayı listesine dönüştürmek istediğimiz bir durumu ele alalım:
Java:
List<Integer> ints = new ArrayList<Integer>(); for (String s : list) { ints.add(Integer.parseInt(s)); }
Ölçek:
val ints = list.map(s => s.toInt)
Scala'nın işlevsel özellikleri sayesinde bu dönüşüm son derece basit hale gelir.
Bir Sınıf Örneği: Java ve Scala
İşleri bir adım daha ileri götürelim ve Java ve Scala'da standart fasulye/düz eski Java nesnesi (POJO) oluşturmayı karşılaştıralım.
İlk olarak, Java sürümü:
public class User { private String name; private List<Order> orders; public User() { orders = new ArrayList<Order>(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Order> getOrders() { return orders; } public void setOrders(List<Order> orders) { this.orders = orders; } } public class Order { private int id; private List<Product> products; public Order() { products = new ArrayList<Product>(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public List<Product> getProducts() { return products; } public void setProducts(List<Product> products) { this.products = products; } } public class Product { private int id; private String category; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } }
Vay canına. Lotta kodu.
Şimdi Scala versiyonu:

class User { var name: String = _ var orders: List[Order] = Nil } class Order { var id: Int = _ var products: List[Product] = Nil } class Product { var id: Int = _ var category: String = _ }
Hangi dilin daha karmaşık olduğunu söylemiştik?!
Adil miyiz?
Buraya kadar geldiyseniz ve bir Java programcısıysanız, bu noktada haksız bir kod karşılaştırması yaptığımı düşünüyor olabilirsiniz. Sonuçta, beni Java'da genel değişkenler yapmaktan ve ardından alıcılardan ve ayarlayıcılardan kurtulmaktan alıkoyan hiçbir şey yok.
Bununla birlikte, Java'daki alıcıların ve ayarlayıcıların arkasındaki mantığa geri dönerseniz, bu özellikle geleceğe yöneliktir. Yani, daha sonra değişkenlerin alınmasına veya ayarlanmasına bir mantık eklemeniz gerekirse, bunun yerine yöntemleri kullanmak için bu genel değişkenleri yeniden yazmanız gerekir (bu nedenle, başlangıçta alıcıların ve ayarlayıcıların kullanılması Java'da teşvik edilir). ). Ancak, Scala programlamasında durum böyle değildir. Dil tasarımı nedeniyle, soyutlama alıcılara ve ayarlayıcılara ihtiyaç duymadan bozulmadan kalır. Örneğin, adı null olarak ayarlamaya çalışırsanız, Scala'da bir NullPointerException
oluşturan bu değiştirilmiş User
sınıfını düşünün:
class User { private var _name: String = _ var orders: List[Order] = Nil def name = _name def name_=(name: String) = { if (name == null) { throw new NullPointerException("User.name cannot be null!") } _name = name }
Ve yine de adı şu şekilde ayarlayabilirsiniz:
user.name = "John Doe"
Bunun, yöntem erişimcilerini önceden yapılandırma ihtiyacını tamamen ortadan kaldırdığını unutmayın.
Ayrıca, Scala değişmezliği tercih ettiğinden, bunu Scala'da vaka sınıflarıyla daha da kısaca yazabilirim:
case class User(name: String, orders: List[Order]) case class Order(id: Int, products: List[Product]) case class Product(id: Int, category: String)
Ne kadar az kod yazmam gerektiği çok çılgınca.
Örneği biraz daha ileri götürmek
Şimdi, User
sınıfına, User
sipariş ettiği tüm Products
bir listesini döndüren küçük, şık bir yöntem eklemek istediğim yukarıdaki sınıflarla ilgili bir senaryo düşünün:
Java'nın ayrıntılı dünyasında:
public List<Product> getProducts() { List<Product> products = new ArrayList<Product>(); for (Order order : orders) { products.addAll(order.getProducts()); } return products; }
Neyse ki, java.util.List
bir addAll yöntemi vardır, yoksa addAll
getProducts()
Java'da daha da uzun olurdu.
Öte yandan Scala'da ihtiyacımız olan tek şey:
def products = orders.flatMap(o => o.products)
Scala dil uygulamasının ne kadar küçük olduğunu görebilirsiniz. Evet, Scala'ya yeni başlayanlar için daha karmaşık görünebilir, ancak arkasındaki kavramları gerçekten tam olarak anladığınızda, Scala kodu Java kodundan çok daha basit görünecektir.
Burada biraz daha karmaşık olalım. Products
yalnızca belirli bir Category
almak istersek ne olur?
Bu durumda, java.util.List
içindeki addAll
yönteminden yararlanamayız, bu nedenle Java'da işler daha da çirkinleşir:
public List<Product> getProductsByCategory(String category) { List<Product> products = new ArrayList<Product>(); for (Order order : orders) { for (Product product : order.getProducts()) { if (category.equals(product.getCategory())) { products.add(product); } } } return products; }
Ancak Scala'da kod oldukça basit kalır. Düzleştirilmiş her bir Order
ürün listelerini tek bir listede birleştirmek için flatMap
kullanırız, ardından yalnızca kategoriyle eşleşenleri içerecek şekilde filtreleriz:
def productsByCategory(category: String) = orders.flatMap(o => o.products).filter(p => p.category == category)
Dinamik ve statik
Son birkaç yılda kesinlikle yeni dil sıkıntısı yaşanmadı, ancak yakın zamanda ortaya çıkan diğer dillerin neredeyse tamamı dinamikken, Scala statik olarak yazılmıştır.
Profesyonel bir geliştirici olarak - pek çok dinamik dil bilmeme ve kullanmama rağmen - bence sağlam kod yazmak için derleme zamanı kontrolleri inanılmaz derecede önemli. Dinamik bir dilde, kodunuzu çok çeşitli senaryolarda çalıştırmadan kodunuzun yeterince hatasız ve sağlam olduğundan asla emin olamazsınız. Bu, kod üretime girene kadar asla fark edilmeyen kodda potansiyel olarak ciddi kusurlara yol açabilir.
Sarmak
Umarım bu makale, Scala'nın gücü ve yetenekleri hakkında size bir ön fikir verecek kadar Java ile Scala'yı karşılaştırır ve dil öğrenme iştahınızı kabartır. Sadece programlamayı daha az sıkıcı ve daha eğlenceli hale getiren harika bir dil olmakla kalmaz, aynı zamanda dünyanın en büyük şirketlerinden bazıları (LinkedIn, Twitter, FourSquare, The Guardian) tarafından da kullanılmaktadır.
Scala geliştiricileri için sürekli artan açık pozisyon sayısının kanıtladığı gibi, Scala'nın popülaritesi ve kullanımı hızla artıyor. Henüz yapmadıysanız, şimdi dalgayı sürmeye başlamanın ve “Neden Scala'yı öğrenin?” Diye sormayı bırakmanın tam zamanı olacaktır.