ฉันกำลังมองหาการใช้งานฟังก์ชั่นในรายการวัตถุเช่นเดียวกับใน C # โดยใช้วิธีการขยาย
บางสิ่งเช่นนี้
List<DataObject> list;
// ... List initialization.
list.getData(id);
ฉันจะทำสิ่งนั้นใน Java ได้อย่างไร
ฉันกำลังมองหาการใช้งานฟังก์ชั่นในรายการวัตถุเช่นเดียวกับใน C # โดยใช้วิธีการขยาย
บางสิ่งเช่นนี้
List<DataObject> list;
// ... List initialization.
list.getData(id);
ฉันจะทำสิ่งนั้นใน Java ได้อย่างไร
คำตอบ:
Java ไม่รองรับวิธีการขยาย
แต่คุณสามารถสร้างวิธีการแบบสแตติกปกติหรือเขียนคลาสของคุณเอง
วิธีการขยายไม่เพียง แต่เป็นวิธีการคงที่และไม่เพียง แต่ความสะดวกสบายไวยากรณ์น้ำตาลในความเป็นจริงพวกเขาเป็นเครื่องมือที่มีประสิทธิภาพมาก สิ่งสำคัญที่มีความสามารถในการแทนที่วิธีการต่าง ๆ ขึ้นอยู่กับการสร้างอินสแตนซ์ของพารามิเตอร์ต่าง ๆ นี่คล้ายกับคลาสประเภทของ Haskell และที่จริงแล้วดูเหมือนว่าพวกเขาจะอยู่ใน C # เพื่อสนับสนุน Monads ของ C # (เช่น LINQ) แม้การทิ้งไวยากรณ์ของ LINQ ฉันยังไม่รู้วิธีการใช้อินเทอร์เฟซที่คล้ายคลึงกันใน Java
และฉันคิดว่ามันเป็นไปไม่ได้ที่จะนำไปใช้ใน Java เพราะความหมายของการลบประเภทของจาวาของพารามิเตอร์ทั่วไป
โปรเจ็กต์ลอมบอกมีคำอธิบายประกอบ@ExtensionMethod
ที่สามารถใช้เพื่อให้บรรลุการทำงานที่คุณต้องการ
java.lang.String
ช่วยให้คุณสามารถเพิ่มวิธีการเรียนที่คุณไม่สามารถควบคุมเช่น การสาธิต: http://manifold.systems/images/ExtensionMethod.mp4
ในทางเทคนิค C # Extension นั้นไม่เทียบเท่าใน Java แต่ถ้าคุณต้องการที่จะใช้ฟังก์ชั่นดังกล่าวสำหรับโค้ดที่สะอาดกว่าและการบำรุงรักษาคุณต้องใช้เฟรมเวิร์ก Manifold
package extensions.java.lang.String;
import manifold.ext.api.*;
@Extension
public class MyStringExtension {
public static void print(@This String thiz) {
System.out.println(thiz);
}
@Extension
public static String lineSeparator() {
return System.lineSeparator();
}
}
เอ็กซ์เท็นด์ภาษา - ซึ่งเป็นซุปเปอร์ชุดของ Java และคอมไพล์ชวารหัสที่มา1 - รองรับนี้
Manifold นำเสนอ Java ด้วยวิธีการขยาย C # -style และคุณสมบัติอื่น ๆ ซึ่งแตกต่างจากเครื่องมืออื่น ๆ Manifold มีข้อ จำกัด และไม่ต้องทนทุกข์ทรมานจากปัญหาเกี่ยวกับข้อมูลทั่วไป, lambdas, IDE ฯลฯ Manifold มีคุณสมบัติอื่น ๆ เช่น F # สไตล์ประเภทที่กำหนดเอง , typescript สไตล์การเชื่อมต่อโครงสร้างและ Javascript สไตล์ประเภท Expando
นอกจากนี้ IntelliJ ให้การสนับสนุนที่ครอบคลุมสำหรับ Manifold ผ่าน Manifold ปลั๊กอิน
อีกตัวเลือกหนึ่งคือใช้คลาสForwardingXXXจากไลบรารี google-guava
Java ไม่มีคุณสมบัติดังกล่าว แต่คุณสามารถสร้างคลาสย่อยปกติของการใช้งานรายการของคุณหรือสร้างคลาสภายในที่ไม่ระบุชื่อ:
List<String> list = new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
};
ปัญหาคือการเรียกวิธีนี้ คุณสามารถทำได้ "ในสถานที่":
new ArrayList<String>() {
public String getData() {
return ""; // add your implementation here.
}
}.getData();
ดูเหมือนว่ามีบางโอกาสเพียงเล็กน้อยที่ Defender วิธี (วิธีการเริ่มต้น IE) อาจจะทำให้มันกลายเป็น Java 8 อย่างไรก็ตามเท่าที่ผมเข้าใจพวกเขาพวกเขาเท่านั้นที่อนุญาตให้ผู้เขียนของinterface
ย้อนหลังขยายมันไม่ผู้ใช้โดยพลการ
Defender Methods + Interface Injection จะสามารถใช้วิธีการขยาย C # -style ได้อย่างเต็มที่ แต่ AFAICS การฉีดอินเตอร์เฟสยังไม่ได้อยู่ในแผนที่ถนน Java 8
สายไปปาร์ตี้กับคำถามนี้ แต่ในกรณีที่ใคร ๆ พบว่ามีประโยชน์ฉันเพิ่งสร้างคลาสย่อย:
public class ArrayList2<T> extends ArrayList<T>
{
private static final long serialVersionUID = 1L;
public T getLast()
{
if (this.isEmpty())
{
return null;
}
else
{
return this.get(this.size() - 1);
}
}
}
เราสามารถจำลองการใช้งานวิธีการขยาย C # ใน Java โดยใช้วิธีการเริ่มต้นที่มีอยู่ตั้งแต่ Java 8 เราเริ่มต้นด้วยการกำหนดอินเตอร์เฟสที่จะช่วยให้เราสามารถเข้าถึงวัตถุสนับสนุนผ่านวิธีการ base () ดังนี้:
public interface Extension<T> {
default T base() {
return null;
}
}
เราคืนค่า null เนื่องจากอินเตอร์เฟสไม่สามารถระบุสถานะได้ แต่จะต้องแก้ไขในภายหลังผ่านทางพร็อกซี
ผู้พัฒนาส่วนขยายจะต้องขยายส่วนต่อประสานนี้โดยส่วนต่อประสานใหม่ที่มีวิธีการส่วนขยาย สมมติว่าเราต้องการเพิ่มผู้บริโภคแต่ละรายในส่วนติดต่อรายการ:
public interface ListExtension<T> extends Extension<List<T>> {
default void foreach(Consumer<T> consumer) {
for (T item : base()) {
consumer.accept(item);
}
}
}
เนื่องจากเราขยายส่วนต่อขยายส่วนขยายเราสามารถเรียกใช้เมธอด base () ภายในวิธีส่วนขยายของเราเพื่อเข้าถึงวัตถุสนับสนุนที่เราแนบ
ส่วนต่อขยายส่วนต่อขยายต้องมีวิธีการจากโรงงานซึ่งจะสร้างส่วนขยายของวัตถุสนับสนุนที่ระบุ:
public interface Extension<T> {
...
static <E extends Extension<T>, T> E create(Class<E> type, T instance) {
if (type.isInterface()) {
ExtensionHandler<T> handler = new ExtensionHandler<T>(instance);
List<Class<?>> interfaces = new ArrayList<Class<?>>();
interfaces.add(type);
Class<?> baseType = type.getSuperclass();
while (baseType != null && baseType.isInterface()) {
interfaces.add(baseType);
baseType = baseType.getSuperclass();
}
Object proxy = Proxy.newProxyInstance(
Extension.class.getClassLoader(),
interfaces.toArray(new Class<?>[interfaces.size()]),
handler);
return type.cast(proxy);
} else {
return null;
}
}
}
เราสร้างพร็อกซีที่ใช้ส่วนขยายส่วนต่อประสานและส่วนต่อประสานทั้งหมดที่ใช้งานโดยประเภทของวัตถุสนับสนุน ตัวจัดการการเรียกใช้ที่กำหนดให้กับพร็อกซีจะส่งการเรียกไปยังวัตถุสนับสนุนทั้งหมดยกเว้นวิธี "ฐาน" ซึ่งจะต้องส่งคืนวัตถุสนับสนุนมิฉะนั้นการใช้งานเริ่มต้นจะส่งคืน null
public class ExtensionHandler<T> implements InvocationHandler {
private T instance;
private ExtensionHandler(T instance) {
this.instance = instance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("base".equals(method.getName())
&& method.getParameterCount() == 0) {
return instance;
} else {
Class<?> type = method.getDeclaringClass();
MethodHandles.Lookup lookup = MethodHandles.lookup()
.in(type);
Field allowedModesField = lookup.getClass().getDeclaredField("allowedModes");
makeFieldModifiable(allowedModesField);
allowedModesField.set(lookup, -1);
return lookup
.unreflectSpecial(method, type)
.bindTo(proxy)
.invokeWithArguments(args);
}
}
private static void makeFieldModifiable(Field field) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField
.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
}
จากนั้นเราสามารถใช้ Extension.create () วิธีการเชื่อมต่ออินเตอร์เฟซที่มีวิธีการขยายไปยังวัตถุสนับสนุน ผลลัพธ์คือวัตถุที่สามารถส่งไปยังส่วนต่อขยายที่เรายังสามารถเข้าถึงวัตถุสนับสนุนที่เรียกใช้เมธอด base () ขณะนี้เราสามารถเรียกวิธีการขยายที่สามารถเข้าถึงวัตถุสนับสนุนได้อย่างปลอดภัยดังนั้นตอนนี้เราจึงสามารถแนบวิธีการใหม่เข้ากับวัตถุที่มีอยู่ได้ แต่ไม่ใช่ประเภทการกำหนด:
public class Program {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c");
ListExtension<String> listExtension = Extension.create(ListExtension.class, list);
listExtension.foreach(System.out::println);
}
}
ดังนั้นนี่คือวิธีที่เราสามารถจำลองความสามารถในการขยายวัตถุใน Java โดยการเพิ่มสัญญาใหม่ให้กับพวกเขาซึ่งช่วยให้เราสามารถเรียกวิธีการเพิ่มเติมเกี่ยวกับวัตถุที่กำหนด
ด้านล่างคุณอาจพบรหัสของส่วนต่อขยายที่ต่อไปนี้:
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public interface Extension<T> {
public class ExtensionHandler<T> implements InvocationHandler {
private T instance;
private ExtensionHandler(T instance) {
this.instance = instance;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("base".equals(method.getName())
&& method.getParameterCount() == 0) {
return instance;
} else {
Class<?> type = method.getDeclaringClass();
MethodHandles.Lookup lookup = MethodHandles.lookup()
.in(type);
Field allowedModesField = lookup.getClass().getDeclaredField("allowedModes");
makeFieldModifiable(allowedModesField);
allowedModesField.set(lookup, -1);
return lookup
.unreflectSpecial(method, type)
.bindTo(proxy)
.invokeWithArguments(args);
}
}
private static void makeFieldModifiable(Field field) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
}
default T base() {
return null;
}
static <E extends Extension<T>, T> E create(Class<E> type, T instance) {
if (type.isInterface()) {
ExtensionHandler<T> handler = new ExtensionHandler<T>(instance);
List<Class<?>> interfaces = new ArrayList<Class<?>>();
interfaces.add(type);
Class<?> baseType = type.getSuperclass();
while (baseType != null && baseType.isInterface()) {
interfaces.add(baseType);
baseType = baseType.getSuperclass();
}
Object proxy = Proxy.newProxyInstance(
Extension.class.getClassLoader(),
interfaces.toArray(new Class<?>[interfaces.size()]),
handler);
return type.cast(proxy);
} else {
return null;
}
}
}
หนึ่งอาจจะใช้มัณฑนากรรูปแบบการออกแบบเชิงวัตถุ ตัวอย่างของรูปแบบนี้ที่ใช้ในไลบรารีมาตรฐานของ Java คือDataOutputStream DataOutputStream
นี่คือรหัสบางส่วนสำหรับเพิ่มฟังก์ชั่นของรายการ:
public class ListDecorator<E> implements List<E>
{
public final List<E> wrapee;
public ListDecorator(List<E> wrapee)
{
this.wrapee = wrapee;
}
// implementation of all the list's methods here...
public <R> ListDecorator<R> map(Transform<E,R> transformer)
{
ArrayList<R> result = new ArrayList<R>(size());
for (E element : this)
{
R transformed = transformer.transform(element);
result.add(transformed);
}
return new ListDecorator<R>(result);
}
}
PS ฉันเป็นแฟนตัวยงของKotlin มันมีวิธีการขยายและยังทำงานบน JVM
คุณสามารถสร้างวิธี C / like extension / helper โดย (RE) การนำอินเตอร์เฟส Collections ไปใช้และเพิ่มตัวอย่างสำหรับ Java Collection:
public class RockCollection<T extends Comparable<T>> implements Collection<T> {
private Collection<T> _list = new ArrayList<T>();
//###########Custom extension methods###########
public T doSomething() {
//do some stuff
return _list
}
//proper examples
public T find(Predicate<T> predicate) {
return _list.stream()
.filter(predicate)
.findFirst()
.get();
}
public List<T> findAll(Predicate<T> predicate) {
return _list.stream()
.filter(predicate)
.collect(Collectors.<T>toList());
}
public String join(String joiner) {
StringBuilder aggregate = new StringBuilder("");
_list.forEach( item ->
aggregate.append(item.toString() + joiner)
);
return aggregate.toString().substring(0, aggregate.length() - 1);
}
public List<T> reverse() {
List<T> listToReverse = (List<T>)_list;
Collections.reverse(listToReverse);
return listToReverse;
}
public List<T> sort(Comparator<T> sortComparer) {
List<T> listToReverse = (List<T>)_list;
Collections.sort(listToReverse, sortComparer);
return listToReverse;
}
public int sum() {
List<T> list = (List<T>)_list;
int total = 0;
for (T aList : list) {
total += Integer.parseInt(aList.toString());
}
return total;
}
public List<T> minus(RockCollection<T> listToMinus) {
List<T> list = (List<T>)_list;
int total = 0;
listToMinus.forEach(list::remove);
return list;
}
public Double average() {
List<T> list = (List<T>)_list;
Double total = 0.0;
for (T aList : list) {
total += Double.parseDouble(aList.toString());
}
return total / list.size();
}
public T first() {
return _list.stream().findFirst().get();
//.collect(Collectors.<T>toList());
}
public T last() {
List<T> list = (List<T>)_list;
return list.get(_list.size() - 1);
}
//##############################################
//Re-implement existing methods
@Override
public int size() {
return _list.size();
}
@Override
public boolean isEmpty() {
return _list == null || _list.size() == 0;
}
Java
8 ตอนนี้รองรับวิธีการเริ่มต้นซึ่งคล้ายกับC#
วิธีการขยายของ