1. Java / Говнокод #12960

    +113

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    76. 76
    77. 77
    78. 78
    79. 79
    80. 80
    81. 81
    82. 82
    83. 83
    84. 84
    85. 85
    86. 86
    87. 87
    88. 88
    89. 89
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    
    public class Pause extends JFrame{
    
    	private int MAX;
    	private static String s;
    	
    	Pause(){
    		this.setSize(300, 300);
    		this.setVisible(true);
    	}
    
    	public void text(String s, final Thread t){
    		try {
    			t.wait();
    		} catch (InterruptedException e1) {
    			// TODO Auto-generated catch block
    			e1.printStackTrace();
    		}
    		JLabel l = new JLabel(s);
    		l.addMouseListener(new MouseListener() {
    			
    			@Override
    			public void mouseClicked(MouseEvent e) {
    				// TODO Auto-generated method stub
    				t.notify();
    			}
    			@Override
    			public void mousePressed(MouseEvent e) {
    				// TODO Auto-generated method stub
    			}
    			@Override
    			public void mouseReleased(MouseEvent e) {
    				// TODO Auto-generated method stub
    			}
    			@Override
    			public void mouseEntered(MouseEvent e) {
    				// TODO Auto-generated method stub
    			}
    			@Override
    			public void mouseExited(MouseEvent e) {
    				// TODO Auto-generated method stub
    			}
    		});
    		this.add(l);
    	}
    	
    	public int getMAX() {
    		return MAX;
    	}
    
    	public void setMAX(int mAX) {
    		MAX = mAX;
    	}
    	
    	public String getS() {
    		return s;
    	}
    
    	public void setS(String s) {
    		this.s = s;
    	}
    
    	public void appendS(String s) {
    		this.s += s;
    	}
    
    
    	public static void main(final String[] args){
    		final Pause p = new Pause();
    		final Thread t = new Thread(new Runnable() {
    			
    			public void run() {
    				
    				for (int i = 0; i < p.getMAX(); ++i){
    					p.appendS(i + " ");
    					if (i+2 <= p.getMAX()){
    						p.text(s, t); //error
    					}
    				}
    			}
    		});
    		t.start();
    	}
    }

    Прислала подруга, изучающая потоки в Java, с просьбой подсказать, почему выдаётся ошибка компиляции при использовании переменной t внутри run.

    Придётся объяснять, что в этом коде неправильно вообще всё, от первой до последней строки...

    Запостил: someone, 03 Мая 2013

    Комментарии (39) RSS

    • А кстати, ОП, почему?
      t жеж final, компилятор вполне же мог сообразить, что это просто синоним this.
      Ответить
      • Использование в теле анонимного класса считается использованием переменной до инициализации. Что логично - инициализация переменной t будет закончана только по завершении инструкции присваивания.
        Ответить
        • Ну это как бы объясняет почему не получилось (но это ж не неразрешимая проблема, имена методов он же находит в любом порядке). Мог бы и тут постараться немного.
          Ответить
          • Я подозреваю, что причина тут в том, что в общем случае нельзя гарантировать, что метод, в котором используется неинициализированная переменная, не будет вызван в момент инициализации (например, из конструктора).

            Например, представьте, что в данном примере метод run вызывается из конструктора класса Thread (хоть это и не так, но компилятор об этом не знает). Тогда будет произведено обращение к переменной t до завершения создания объекта и тем более до того, как объект будет ей присвоен.
            Ответить
            • Ну это если смотреть на присваивание как на операцию, которую нужно как-то делать (как в С++). В Яве в присваивании ничего не сделать - в этом примере это, по сути, просто лексическое связывание, и технически ничего не мешает скомпилировать такой код всегда правильно. Почему я особенно бы рассчитывал на final в таком случае - он мог бы как раз помочь отличить лексическое связывание (просто ярлык для того, что находится справа от "=") от чего-то, что где-то по ходу программы будет еще менятся.
              Ответить
              • Я только что объяснила, почему это в общем случае невозможно.

                Как должен вести себя компилятор?

                Вот такой пример не компилируется:

                public abstract class Test {
                	public Test() {
                		run();
                	}
                	
                	public abstract void run();
                
                	public static void main(final String[] args) {
                		final Test t = new Test() {
                			@Override
                			public void run() {
                				System.out.println(t);
                			}
                		};
                	}
                
                }


                Что бы вывелось, если бы он компилировался?
                Ответить
                • В общем случае - да, возможно, просто нужно уточнять семантику того, что подразумевается под final. То, что пример не компилируется - это следствие того, что компилятор его не может скомпилировать, а не того, что он в принципе не может быть скомпилирован.

                  > что бы вывелось?
                  Точно то же самое, что и при попытке распечатать this. Почему так? - потому что t - ето просто лексическое связывание, и this тождественно t.
                  Но если бы мы говорили об операции присваивания (и ситуация в которой t и this Test'a различны имела бы смысл), то тогда, конечно, так нельзя делать.

                  По крайней мере, мой аргумент такой, что новичек, пусть и не осознано, но нашел нетривиальный случай, где размыта семантика языка, и причины по которым мжно было бы сделать так, как автору хотелось, мне видятся вполне легитимными.
                  Ответить
                  • В общем случае - не взлетит.

                    Хорошо, в данном случае компилятор может "догадаться" и заменить t на this (кстати, почему бы тогда программисту сразу и не написать this?). Но в общем случае так поступить нельзя.

                    Например, в приведённом выше коде участвуют два объекта: Thread и Runnable, при этом в тело объекта Runnable передаётся переменная типа Thread. Тут уже this не отделаешься. В момент конструирования объекта Runnable объект Thread ещё не существует.
                    Ответить
                    • Нет, вы все время пытаетесь перейти на детали реализации.
                      Во-первых, проблема, которую вы описываете - решаемая.
                      Во-вторых, вы игнорируете неоднозначность конструкции a = b в Яве. Неоднозначность заключается в том, что иногда ее можно воспринять как разрушающее присваивание, а иногда, как выражение тождества. В первом случае вы бы были правы. Во втором - нет. final имеет отличительную особенность - переменным с таким модификаторм нельзя ничего позже присваивать, и поэтому, интуитивно, хочется верить, что они выражают тождество.
                      Как вы это тождество реализуете? - уже ваши проблемы, но принципиально можно показать, что это всегда реализуемо.

                      Это та же проблема, которая возникает при определении рекурсии:
                      void f () { f(); }

                      Ведь вы же не считаете эту конструкцию невозможной потому что внутри f f "еще не известно".
                      Ответить
                      • Как реализовать рекурсию, понятно.

                        В Java - invokevirtual/invokeinterface/invokestatic/invokespecial и пусть дальше JVM разбирается.

                        В нативном коде компилятор в момент генерации тела функции уже знает её адрес или по крайней мере смещение от текущей инструкции.

                        Как реализовать вышеупомянутую конструкцию - совершенно непонятно.

                        Вот ещё кусок кода, Фома ты неверующий:

                        public abstract class Inner implements Runnable {
                         public Inner() { run(); }
                        }
                        
                        public class Test {
                        	public Test(Inner i) {
                        		i.run();
                        	}
                        
                        	public static void main(final String[] args) {
                        		final Test t = new Test(new Inner() {
                        			@Override
                        			public void run() {
                        				System.out.println(t);
                        			}
                        		});
                        	}
                        
                        }


                        Что прикажете делать компилятору? Ведь объекты создаются в строгой последовательности: сначала Inner, потом t. В момент выполнения конструктора объекта Inner объект t ещё не существует и, соответственно, не может быть передан по ссылке. А ссылка-то уже нужна!
                        Ответить
                        • Еще раз, t тождественно this, если мы считаем, что final - это такой способ выразить тождество. Следовательно, везде, где можно подставить this, t имело бы точно тот же смысл.

                          Вот, смотрите:
                          (* Declares class `test' with two methods: *)
                          (* `test' and `printme' *)
                          class test =
                          object
                            method test = (Printf.printf "Who are you? - %s\n")
                            method printme = "I am t"
                          end;;
                          
                          (* lexically binds `t' to the newly created instance of `test' *)
                          let t = new test in t#test (t#printme);;

                          И работает ведь! ;)
                          Ответить
                          • Я ему про Фому, он мне про Ерёму.

                            В последнем моём примере t не тождественно this. Потому что внутри объекта Inner this - это объект Inner, а t - ещё не созданный объект Test.
                            Ответить
                            • Я же говорю ... в которой t и this Test'a.... И, опять же, вы говорите, что это не возможно потому что компилятор так не умеет - и тут я с вами согласен. Но когда вы гороворите, что так нельзя в принципе, то тут вы ошибаетесь, потому что в принципе так можно, тому я вам привел наглядный пример. другой язык, другой компилятор такое позволяет делать.
                              Ответить
                            • Даже, пожалуй, пример был не совсем удачным, вот лучше будет:
                              let test x =
                              object
                                method printme = "I'm test"
                              end in
                              Printf.printf "printme: %s\n" (test test)#printme;;
                              Ответить
                              • .
                                Ответить
                                • Какой это язык?
                                  Ответить
                                  • ОКамл, только я сейчас начал понимать, что код не эквивалентный... :) А эквивалентный что-то не получается.

                                    Или имеется в виду "."? - тогда Баш, наверное.
                                    Ответить
    • >swing
      Но зачем? Он же уебищен. Как и почти весь гуй на яве, но хотя бы есть SWT.
      Ответить
      • Мне когда-то давно в универе на курсе Явы дали какую-то книжку с примерами + MSI установщиком JDK... Там почему-то все премеры были сделаны на Swing.
        Ответить
        • В то время было что-то другое?
          Ответить
          • Да, уже было, клипсу уже несколько лет тогда было. Просто преподаватель был ничем не интересующийся старпер, который принимал лабы только в распечатках, и даже не запускал никогда.
            Ответить
            • Вот ты сам на свой вопрос и ответил. Нам тоже давали swing, хз почему, но факт. Может потому, что препод был макоебом, а там он не настолько уебищен?
              Ответить
              • На какой вопрос?
                Ответить
                • >Там почему-то все премеры были сделаны на Swing.
                  Потому что всем похуй. И мб SWT тогда еще не вырос.
                  Ответить
      • Чем Swing хуже SWT?
        Ответить
        • Тем, что под виндой он - ебаное говно?
          Ответить
          • Почему?
            Ответить
            • Я не знаю почему, но это так. Выглядит, как говно из 2000-х, даже хуже нативного гуя винды родом из windows 95.
              Ответить
              • Используйте нормальный look and feel. Nimbus, например, или третьей стороны.

                У Swing и SWT разные области применения. Swing (или его современная замена - JavaFX) хорош для приложений, где важен одинаковый вид приложения на разных платформах. SWT - для нативного вида.
                Ответить
                • >одинаковый вид приложения на разных платформах.
                  Одинаковое говно на всех платформах, по крайней мере, на винде точно. Может одинаково мако- или прыщеблядский? Я еще не видел нормально смотрящегося на винде кроссплатформенного гуя. Или менее хуевый (QT, SWT), или совсем хуевый (GTK, все оставшиеся варианты явагуя). Ах да, swing еще и тормозной.

                  А что за look and feel и как его менять?
                  Ответить
                  • http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
                    Ответить
                  • Swing тормозной? В таком случае SWT будет ещё тормознее. Хотя бы потому, что SWT использует "тяжёлые" нативные виджеты, а Swing - "лёгкие", которые рисует сам.

                    Look and feel - тема оформления Swing. Кстати, конкретно под Windows есть и нативная тема оформления Swing. Под Linux/GTK теоретически тоже есть, но выглядит убого.

                    Как менять - погуглите на тему Nimbus.
                    Ответить
      • свинг - прекрасен. кодить его намного легче, чем свт.
        Ответить
        • > свинг - прекрасен
          В зависимости от того, кто его танцует
          Ответить
          • Приверженцы свинга называют себя свингерами. Для облегчения общения с единомышленниками и поиска новых партнёров свингеры объединяются в свинг-клубы, в которые, как правило, принимаются уже сложившиеся пары.
            Ответить
    • просто в жаве есть жесткое правило: никаких ссылок вперед. нельзя ссылаться на еще необъявленную переменную. нельзя объявить константу или переменную, ссылаясь на константу, объявленную позже.
      возможно, это связано с однопроходным анализатором.
      Ответить
    • улыбнули override-ы
      Ответить

    Добавить комментарий