سؤال

لدي طريقة هذا عن عشرة أسطر من التعليمات البرمجية.أريد إنشاء المزيد من الطرق التي تفعل نفس الشيء بالضبط ، باستثناء الصغيرة الحساب الذي يحدث تغيير سطر واحد من التعليمات البرمجية.هذا هو التطبيق الأمثل يمر في مؤشر دالة استبدال سطر واحد ، ولكن جافا لا مؤشرات الدالة.ما هو أفضل بديل ؟

هل كانت مفيدة؟

المحلول

الطبقة الداخلية من مجهول

ويقول كنت تريد أن يكون لها وظيفة مرت في مع String المعلمة التي بإرجاع int.
أولا عليك أن تحدد واجهة مع وظيفة فقط الأعضاء, إذا لم تتمكن من إعادة استخدام قائمة واحدة.

interface StringFunction {
    int func(String param);
}

الأسلوب الذي يأخذ المؤشر ستقبل فقط StringFunction على سبيل المثال مثل ذلك:

public void takingMethod(StringFunction sf) {
   int i = sf.func("my string");
   // do whatever ...
}

و أن يسمى مثل ذلك:

ref.takingMethod(new StringFunction() {
    public int func(String param) {
        // body
    }
});

تحرير: في جافا 8, هل يمكن أن نسميها امدا التعبير:

ref.takingMethod(param -> bodyExpression);

نصائح أخرى

لكل وظيفة "مؤشر" ، أود إنشاء صغير functor الدرجة التي تطبق الحساب الخاص بك.تحديد واجهة جميع الفئات التي سوف تنفذ و تمر حالات من تلك الكائنات في أكبر وظيفة.هذا هو مزيج من "نمط القيادة"،و "نمط استراتيجية".

@sblundy مثال جيد.

عندما يكون هناك عدد محدد مسبقا من عمليات حسابية مختلفة يمكنك القيام به في سطر واحد ، باستخدام enum هو وسيلة سريعة واضحة حتى الآن طريقة لتنفيذ استراتيجية نمط.

public enum Operation {
    PLUS {
        public double calc(double a, double b) {
            return a + b;
        }
    },
    TIMES {
        public double calc(double a, double b) {
            return a * b;
        }
    }
     ...

     public abstract double calc(double a, double b);
}

ومن الواضح أن استراتيجية طريقة الإعلان ، وكذلك بالضبط مثيل واحد من كل تنفيذ كل تعريف في فئة واحدة/الملف.

تحتاج إلى إنشاء واجهة توفر وظيفة(ق) التي تريد تمريرها.على سبيل المثال:

/**
 * A simple interface to wrap up a function of one argument.
 * 
 * @author rcreswick
 *
 */
public interface Function1<S, T> {

   /**
    * Evaluates this function on it's arguments.
    * 
    * @param a The first argument.
    * @return The result.
    */
   public S eval(T a);

}

ثم عندما تحتاج إلى تمرير وظيفة, يمكنك تنفيذ تلك الواجهة:

List<Integer> result = CollectionUtilities.map(list,
        new Function1<Integer, Integer>() {
           @Override
           public Integer eval(Integer a) {
              return a * a;
           }
        });

وأخيرا وظيفة الخريطة يستخدم مرت في Function1 على النحو التالي:

   public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn, 
         Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
      Set<K> keySet = new HashSet<K>();
      keySet.addAll(m1.keySet());
      keySet.addAll(m2.keySet());

      results.clear();

      for (K key : keySet) {
         results.put(key, fn.eval(m1.get(key), m2.get(key)));
      }
      return results;
   }

غالبا ما يمكنك استخدام Runnable بدلا من الواجهة الخاصة بك إذا كنت لا تحتاج إلى تمرير في المعلمات ، أو يمكنك استخدام مختلف التقنيات الأخرى لجعل المعلمة عد أقل "ثابت" لكنه عادة مفاضلة مع نوع الأمان.(أو يمكنك تجاوز منشئ وظيفة الخاص بك كائن لتمرير في params بهذه الطريقة..هناك الكثير من النهج بعض الأعمال أفضل في ظروف معينة.)

طريقة المراجع باستخدام :: المشغل

يمكنك استخدام الأسلوب المراجع في طريقة الحجج حيث الأسلوب يقبل واجهة وظيفية.وظيفية واجهة أي واجهة يحتوي على ملخص واحد فقط الطريقة.(أ واجهة وظيفية قد تحتوي على واحد أو أكثر من الطرق الافتراضية أو أساليب ثابتة.)

IntBinaryOperator هو واجهة وظيفية.لها مجردة الأسلوب ، applyAsInt, يقبل اثنين ints كما معالمها بإرجاع int. Math.max يقبل أيضا اثنين ints بإرجاع int.في هذا المثال ، A.method(Math::max); يجعل parameter.applyAsInt إرسال اثنين من المدخلات القيم Math.max والعودة نتيجة ذلك Math.max.

import java.util.function.IntBinaryOperator;

class A {
    static void method(IntBinaryOperator parameter) {
        int i = parameter.applyAsInt(7315, 89163);
        System.out.println(i);
    }
}
import java.lang.Math;

class B {
    public static void main(String[] args) {
        A.method(Math::max);
    }
}

في عام ، يمكنك استخدام:

method1(Class1::method2);

بدلا من:

method1((arg1, arg2) -> Class1.method2(arg1, arg2));

الذي هو اختصار لـ:

method1(new Interface1() {
    int method1(int arg1, int arg2) {
        return Class1.method2(arg1, agr2);
    }
});

لمزيد من المعلومات, انظر ::(مزدوج القولون) مشغل جافا 8 و لغة جافا مواصفات §15.13.

يمكنك أيضا القيام بذلك (التي في بعض نادر مناسبات المنطقي).مسألة (وهذا هو مشكلة كبيرة) هو أن تخسر كل typesafety استخدام فئة/واجهة لديك للتعامل مع هذه القضية حيث الأسلوب لا وجود لها.

فإنه لا يكون "صالح" أن يمكنك تجاهل القيود المفروضة على الوصول استدعاء أساليب خاصة (لا يظهر في المثال ، ولكن يمكنك استدعاء الأساليب التي المترجم عادة لا تسمح لك المكالمة).

مرة أخرى, أنها حالة نادرة أن هذا منطقي ولكن في هذه المناسبات هو أداة لطيفة أن يكون.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Main
{
    public static void main(final String[] argv)
        throws NoSuchMethodException,
               IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        final String methodName;
        final Method method;
        final Main   main;

        main = new Main();

        if(argv.length == 0)
        {
            methodName = "foo";
        }
        else
        {
            methodName = "bar";
        }

        method = Main.class.getDeclaredMethod(methodName, int.class);

        main.car(method, 42);
    }

    private void foo(final int x)
    {
        System.out.println("foo: " + x);
    }

    private void bar(final int x)
    {
        System.out.println("bar: " + x);
    }

    private void car(final Method method,
                     final int    val)
        throws IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        method.invoke(this, val);
    }
}

إذا كان لديك سطر واحد فقط وهو مختلف يمكنك إضافة معلمة مثل العلم و إذا كان(العلم) البيان الذي يدعو سطر واحد أو الآخر.

كنت قد تكون مهتمة أيضا أن تسمع عن العمل على جافا 7 تنطوي الإغلاق:

ما هو الوضع الحالي الإغلاق في جافا ؟

http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures

جافا جديدة 8 واجهات وظيفية و طريقة المراجع باستخدام :: المشغل.

جافا 8 هي قادرة على الحفاظ على أسلوب المراجع ( MyClass::جديد ) مع "@ واجهة وظيفية"المؤشرات.هناك حاجة إلى نفس الأسلوب الاسم فقط نفس طريقة التوقيع المطلوبة.

على سبيل المثال:

@FunctionalInterface
interface CallbackHandler{
    public void onClick();
}

public class MyClass{
    public void doClick1(){System.out.println("doClick1");;}
    public void doClick2(){System.out.println("doClick2");}
    public CallbackHandler mClickListener = this::doClick;

    public static void main(String[] args) {
        MyClass myObjectInstance = new MyClass();
        CallbackHandler pointer = myObjectInstance::doClick1;
        Runnable pointer2 = myObjectInstance::doClick2;
        pointer.onClick();
        pointer2.run();
    }
}

إذا ماذا لدينا هنا ؟

  1. واجهة وظيفية - هذه هي واجهة المشروح أو لا مع @FunctionalInterface, الذي يحتوي على أسلوب واحد فقط الإعلان.
  2. طريقة المراجع - وهذا هو مجرد بناء جملة خاص يبدو مثل هذا ، objectInstance::methodName, لا أكثر ولا أقل.
  3. مثال للاستخدام - مجرد عامل التعيين ثم واجهة استدعاء الأسلوب.

يجب عليك استخدام واجهات وظيفية للمستمعين فقط لذلك!

لأن جميع هذه مؤشرات الدالة هي سيئة حقا بالنسبة مقروئية و القدرة على فهم.بيد أن الأسلوب المباشر المراجع في بعض الأحيان تأتي في متناول يدي ، foreach على سبيل المثال.

هناك عدة مسبقا واجهات وظيفية:

Runnable              -> void run( );
Supplier<T>           -> T get( );
Consumer<T>           -> void accept(T);
Predicate<T>          -> boolean test(T);
UnaryOperator<T>      -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R>         -> R apply(T);
BiFunction<T,U,R>     -> R apply(T, U);
//... and some more of it ...
Callable<V>           -> V call() throws Exception;
Readable              -> int read(CharBuffer) throws IOException;
AutoCloseable         -> void close() throws Exception;
Iterable<T>           -> Iterator<T> iterator();
Comparable<T>         -> int compareTo(T);
Comparator<T>         -> int compare(T,T);

في إصدارات جافا يجب أن تحاول الجوافة المكتبات التي لديها وظائف مماثلة ، وبناء الجملة ، كما أدريان بيتريسكو وقد ذكر أعلاه.

لمزيد من البحوث ننظر جافا 8 Cheatsheet

و بفضل الرجل مع قبعة على لغة جافا مواصفات §15.13 الارتباط.

@sblundy الجواب هو عظيم, ولكن المجهول الداخلية الطبقات اثنين من العيوب الصغيرة الأولية كونها أنها لا تميل إلى أن تكون قابلة لإعادة الاستخدام و الثانوي ضخمة في بناء الجملة.

الشيء الجميل هو أن نمط توسع في فصول كاملة دون أي تغيير في الطبقة الرئيسية (واحد تنفيذ العمليات الحسابية).

عند إنشاء مثيل فئة جديدة يمكنك تمرير المعلمات إلى تلك الدرجة التي يمكن أن تكون بمثابة ثوابت في المعادلة حتى إذا كان واحد من الطبقات الداخلية تبدو مثل هذا:

f(x,y)=x*y

ولكن في بعض الأحيان تحتاج واحد هو أن:

f(x,y)=x*y*2

و ربما الثالث هو:

f(x,y)=x*y/2

بدلا من جعل اثنين مجهول الطبقات الداخلية أو إضافة "العبور" المعلمة يمكن أن تجعل واحدة الدرجة الفعلية التي كنت مثيل النحو التالي:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);

فإنه ببساطة تخزين ثابتة في الصف واستخدامها في الأسلوب المحدد في واجهة.

في الواقع ، إذا كان يعرف أن المهمة لن تكون مخزنة/إعادة استخدامها ، يمكنك أن تفعل ذلك:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);

ولكن ثابتة الطبقات هي أكثر أمانا--أنا لا يمكن أن تأتي مع تبرير لجعل فئة مثل هذه قابلة للتغيير.

أنا حقا فقط بعد هذا لأنني ارتد كلما سمعت الطبقة الداخلية من مجهول--لقد رأيت الكثير من رمز زائدة عن الحاجة التي كان "مطلوبا" لأن أول شيء مبرمج هل كان الذهاب مجهول عندما كان يجب أن تستخدم الدرجة الفعلية و لم يعاد النظر في قراره.

جوجل الجوافة المكتبات, والتي أصبحت شعبية جدا ، عام وظيفة و المسند الكائن الذي عملوا في أجزاء كثيرة من API الخاصة بهم.

يبدو نمط استراتيجية بالنسبة لي.تحقق من fluffycat.com جافا أنماط.

واحدة من الأشياء أنا حقا أفتقد عند البرمجة في جافا وظيفة رد.حالة واحدة حيث تحتاج هذه الاحتفاظ تقديم نفسها بشكل متكرر في معالجة الهرمية حيث تريد تنفيذ بعض الإجراءات المحددة لكل عنصر.مثل المشي شجرة الدليل ، أو تجهيز بنية البيانات.الحد الأدنى بداخلي يكره الحاجة إلى تحديد واجهة ثم التنفيذ لكل حالة على حدة.

يوم واحد وجدت نفسي أتساءل لماذا لا ؟ لدينا طريقة المؤشرات - طريقة كائن.مع تحسين JIT المجمعين ، العاكسة الاحتجاج حقا لا يحمل ضخمة الأداء جزاء بعد الآن.بالإضافة المقبل إلى نسخ ملف من مكان إلى آخر ، فإن تكلفة ينعكس استدعاء الأسلوب تتضاءل أهميتها.

كما توقعت المزيد عن ذلك ، أدركت أن الاستدعاء في OOP النموذج يتطلب ربط كائن وطريقة معا - إدخال كائن رد.

تحقق من بلدي انعكاس على أساس حل رد في جافا.مجانا لأي استخدام.

حسنا, هذا الموضوع هو بالفعل ما يكفي من العمر ، لذلك من المحتمل جدا جوابي هو لا يساعد على السؤال.ولكن لأن هذا الموضوع ساعدني على إيجاد الحل سأضعه هنا على أية حال.

أنا في حاجة إلى استخدام متغير ثابت طريقة معروفة المدخلات المعروف الإخراج (على حد سواء مزدوج).إذا معرفة طريقة حزمة اسم يمكنني العمل على النحو التالي:

java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);

وظيفة يقبل مزدوج واحد كمعلمة.

في حالة محددة لا تمت تهيئته مع

java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);

واحتج عليه في وقت لاحق في الوضع أكثر تعقيدا مع

return (java.lang.Double)this.Function.invoke(null, args);

java.lang.Object[] args = new java.lang.Object[] {activity};
someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);

حيث النشاط التعسفي قيمة مزدوجة.أنا أفكر ربما تفعل هذا أكثر قليلا من التجريد و التعميم ، كما SoftwareMonkey قام به, ولكن حاليا أنا سعيد بما فيه الكفاية مع ما هو عليه.ثلاثة أسطر من التعليمات البرمجية ، أي فئات و واجهات اللازمة, وهذا ليس سيئا جدا.

أن تفعل الشيء نفسه دون واجهات مجموعة من الوظائف:

class NameFuncPair
{
    public String name;                // name each func
    void   f(String x) {}              // stub gets overridden
    public NameFuncPair(String myName) { this.name = myName; }
}

public class ArrayOfFunctions
{
    public static void main(String[] args)
    {
        final A a = new A();
        final B b = new B();

        NameFuncPair[] fArray = new NameFuncPair[]
        {
            new NameFuncPair("A") { @Override void f(String x) { a.g(x); } },
            new NameFuncPair("B") { @Override void f(String x) { b.h(x); } },
        };

        // Go through the whole func list and run the func named "B"
        for (NameFuncPair fInstance : fArray)
        {
            if (fInstance.name.equals("B"))
            {
                fInstance.f(fInstance.name + "(some args)");
            }
        }
    }
}

class A { void g(String args) { System.out.println(args); } }
class B { void h(String args) { System.out.println(args); } }

تحقق من lambdaj

http://code.google.com/p/lambdaj/

ولا سيما جديدة إغلاق ميزة

http://code.google.com/p/lambdaj/wiki/Closures

وسوف تجد للقراءة على طريقة تحديد إغلاق أو وظيفة المؤشر دون خلق لا معنى لها واجهة أو استخدام القبيح الطبقات الداخلية

واو, لماذا لا مجرد إنشاء مندوب الطبقة التي ليس من الصعب كل ذلك بالنظر إلى أنه سبق أن فعلت جافا و استخدامه لتمرير في المعلمة حيث T هو نوع الإرجاع.أنا آسف ولكن مثل C++/C# مبرمج عام فقط تعلم جافا, أريد مؤشرات الدالة لأنها مفيدة جدا.إذا كنت معتادا على أي الطبقة التي تتعامل مع معلومات طريقة يمكنك أن تفعل ذلك.في المكتبات جافا أن يكون جافا.لانغ.تعكس.الأسلوب.

إذا كنت دائما استخدام واجهة ، لديك دائما لتنفيذ ذلك.في eventhandling حقا ليس هناك طريقة أفضل حول تسجيل/إلغاء تسجيل من قائمة معالجات ولكن المندوبين حيث تحتاج إلى تمرير في وظائف لا نوع القيمة ، مما يجعل الطبقة مندوب للتعامل مع ذلك تتفوق واجهة.

لا شيء من جافا 8 إجابات أعطت كامل متماسك سبيل المثال ، حيث يتعلق الأمر هنا.

تعلن الأسلوب الذي يقبل وظيفة "مؤشر" على النحو التالي:

void doCalculation(Function<Integer, String> calculation, int parameter) {
    final String result = calculation.apply(parameter);
}

نسميها خلال توفير وظيفة مع التعبير امدا:

doCalculation((i) -> i.toString(), 2);

إذا كان أي شخص يكافح من أجل تمرير دالة التي تأخذ مجموعة واحدة من المعايير لتحديد سلوكها ولكن مجموعة أخرى من المعلمات على تنفيذ مثل هذا المخطط هو:

(define (function scalar1 scalar2)
  (lambda (x) (* x scalar1 scalar2)))

انظر تمرير وظيفة مع المعلمة-تعريف السلوك في جافا

منذ Java8 ، يمكنك استخدام lambdas أيضا المكتبات الرسمية SE 8 API.

الاستخدام: تحتاج إلى استخدام واجهة واحدة فقط مجردة الأسلوب.جعل مثيل من ذلك (قد تحتاج إلى استخدام واحد java SE 8 قدم بالفعل) مثل هذا:

Function<InputType, OutputType> functionname = (inputvariablename) {
... 
return outputinstance;
}

لمزيد من المعلومات الخروج الوثائق: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

قبل جافا 8, أقرب بديلا عن وظيفة-المؤشر-مثل وظيفة كان مجهول الدرجة.على سبيل المثال:

Collections.sort(list, new Comparator<CustomClass>(){
    public int compare(CustomClass a, CustomClass b)
    {
        // Logic to compare objects of class CustomClass which returns int as per contract.
    }
});

ولكن الآن في جافا 8 لدينا أنيق جدا البديلة المعروفة باسم امدا التعبير, التي يمكن أن تستخدم:

list.sort((a, b) ->  { a.isBiggerThan(b) } );

حيث isBiggerThan هو طريقة في CustomClass.يمكننا أيضا استخدام الأسلوب المراجع هنا:

list.sort(MyClass::isBiggerThan);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top